Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(examples): Implement sponsors (p+r) for sponsor service management #2411

Draft
wants to merge 18 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/gno.land/p/demo/sponsors/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/p/demo/sponsors
29 changes: 29 additions & 0 deletions examples/gno.land/p/demo/sponsors/sponsors.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package sponsors

import (
"std"
"strconv"
)

type Sponsor struct {
Name string
Address std.Address
ApiVersion string
DiscoverURL string
Description string
ContactInfo string
Height int64
}

// Render function to display sponsor information
func (s *Sponsor) Render() string {
str := "## Sponsor: " + s.Name + "\n" +
"\n" +
" * Address: " + s.Address.String() + "\n" +
" * API Version: " + s.ApiVersion + "\n" +
" * Discover URL: " + s.DiscoverURL + "\n" +
" * Description: " + s.Description + "\n" +
" * Contact Info: " + s.ContactInfo + "\n" +
" * Height: " + strconv.Itoa(int(s.Height)) + "\n"
return str
}
7 changes: 7 additions & 0 deletions examples/gno.land/r/demo/sponsors/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module gno.land/r/demo/sponsors

require (
gno.land/p/demo/avl v0.0.0-latest
gno.land/p/demo/sponsors v0.0.0-latest
gno.land/p/demo/testutils v0.0.0-latest
)
142 changes: 142 additions & 0 deletions examples/gno.land/r/demo/sponsors/sponsors.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package sponsors

import (
"std"

"gno.land/p/demo/avl"
"gno.land/p/demo/sponsors"
)

var (
name2Sponsor *avl.Tree
addr2Sponsor *avl.Tree
totalSponsors int
)

func init() {
name2Sponsor = avl.NewTree() // maping name -> Sponsor
addr2Sponsor = avl.NewTree() // maping address -> Sponsor
totalSponsors = 0
}

// RegisterSponsor registers a new sponsor
func RegisterSponsor(name, apiVersion, url, description, contactInfo string) {
caller := std.PrevRealm().Addr()
// assert not already registered.
if found := addr2Sponsor.Has(caller.String()); found {
panic("address already registered")
}

if found := name2Sponsor.Has(name); found {
panic("sponsorName already registered")
}

sponsor := &sponsors.Sponsor{
Address: caller,
Name: name,
ApiVersion: apiVersion,
DiscoverURL: url,
Description: description,
ContactInfo: contactInfo,
Height: std.GetHeight(),
}

name2Sponsor.Set(name, sponsor)
addr2Sponsor.Set(caller.String(), sponsor)

totalSponsors += 1
}

// GetSponsorByAddress retrieves the sponsor by their address
func GetSponsorByAddress(addr std.Address) *sponsors.Sponsor {
value, found := addr2Sponsor.Get(addr.String())
if !found {
return nil
}

return value.(*sponsors.Sponsor)
}

// GetSponsorByName retrieves the sponsor by their name
func GetSponsorByName(name string) *sponsors.Sponsor {
value, found := name2Sponsor.Get(name)
if !found {
return nil
}

return value.(*sponsors.Sponsor)
}

// GetURLByAddress retrieves the sponsor's URL by their address
func GetURLByAddress(addr std.Address) string {
value, found := addr2Sponsor.Get(addr.String())
if !found {
return ""
}

return value.(*sponsors.Sponsor).DiscoverURL
}

// GetURLByName retrieves the sponsor's URL by their name
func GetURLByName(name string) string {
value, found := name2Sponsor.Get(name)
if !found {
return ""
}

return value.(*sponsors.Sponsor).DiscoverURL
}

// GetList retrieves pairs of (name, address) with each sponsor
func GetList() []string {
var sponsorList []string

name2Sponsor.Iterate("", "", func(key string, value interface{}) bool {
sponsor := value.(*sponsors.Sponsor)

sponsorList = append(sponsorList, key, sponsor.Address.String())

return false
})

return sponsorList
}

// GetTotalSponsors returns the total number of registered sponsors
func GetTotalSponsors() int {
return totalSponsors
}

// Render displays information based on the given path
func Render(path string) string {
if path == "" {
return renderHome()
} else if len(path) >= 38 { // 39? 40?
if path[:2] != "g1" {
return "invalid address " + path
}
sponsor := GetSponsorByAddress(std.Address(path))
if sponsor == nil {
// TODO: display basic information about account.
return "unknown address " + path
}
return sponsor.Render()
} else {
sponsor := GetSponsorByName(path)
if sponsor == nil {
return "unknown sponsorname " + path
}
return sponsor.Render()
}
}

// renderHome renders the home page with a list of sponsors
func renderHome() string {
doc := ""
name2Sponsor.Iterate("", "", func(key string, value interface{}) bool {
sponsor := value.(*sponsors.Sponsor)
doc += " * [" + sponsor.Name + "](/r/demo/sponsors:" + sponsor.Name + ")\n"
return false
})
return doc
}
135 changes: 135 additions & 0 deletions examples/gno.land/r/demo/sponsors/sponsors_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package sponsors

import (
"std"
"strconv"
"testing"

"gno.land/p/demo/testutils"
)

// TestSponsors tests the sponsor management functionalities
func TestSponsors(t *testing.T) {
caller := testutils.TestAddress("sponsor1")

std.TestSetRealm(std.NewUserRealm(caller))
std.TestSetOrigCaller(caller) // TODO(bug): should not be needed

// Test registering a new sponsor
RegisterSponsor("Sponsor1", "v1", "http://example.com", "Test Sponsor", "test@example.com")

// Check if the sponsor was registered
sponsor := GetSponsorByName("Sponsor1")
if sponsor == nil {
t.Error("Sponsor1 not found")
}

if sponsor.Name != "Sponsor1" {
t.Errorf("Expected name, got %s", sponsor.Name)
}
if sponsor.ApiVersion != "v1" {
t.Errorf("Expected apiVersion v1, got %s", sponsor.ApiVersion)
}
if sponsor.Address != caller {
t.Errorf("Expected address %s, got %s", caller.String(), sponsor.Address.String())
}
if sponsor.DiscoverURL != "http://example.com" {
t.Errorf("Expected url http://example.com, got %s", sponsor.DiscoverURL)
}
if sponsor.ContactInfo != "test@example.com" {
t.Errorf("Expected contactInfo test@example.com, got %s", sponsor.ContactInfo)
}

// Retrieve the sponsor by address
sponsor = GetSponsorByAddress(caller)
if sponsor == nil {
t.Error("Sponsor1 not found by address")
}

if sponsor.Name != "Sponsor1" {
t.Errorf("Expected name, got %s", sponsor.Name)
}
if sponsor.ApiVersion != "v1" {
t.Errorf("Expected apiVersion v1, got %s", sponsor.ApiVersion)
}
if sponsor.Address != caller {
t.Errorf("Expected address %s, got %s", caller.String(), sponsor.Address.String())
}
if sponsor.DiscoverURL != "http://example.com" {
t.Errorf("Expected url http://example.com, got %s", sponsor.DiscoverURL)
}
if sponsor.ContactInfo != "test@example.com" {
t.Errorf("Expected contactInfo test@example.com, got %s", sponsor.ContactInfo)
}

// Retrieve the sponsor's URL by name
url := GetURLByName("Sponsor1")
if url != "http://example.com" {
t.Error("Sponsor1 not found")
}

// Retrieve the sponsor's URL by address
url = GetURLByAddress(caller)
if url != "http://example.com" {
t.Error("Sponsor1 not found by address")
}

// Test duplicate registration by name
defer func() {
if r := recover(); r == nil {
t.Errorf("Expected panic for duplicate name registration")
}
}()
RegisterSponsor("Sponsor1", "v1", "http://example.com", "Test Sponsor", "test@example.com")

// Retrieve the sponsor by address
sponsor = GetSponsorByAddress(caller)
if sponsor == nil || sponsor.Name != "Sponsor1" {
t.Errorf("Sponsor1 not found by address")
}

// Test the GetTotalSponsors function
RegisterSponsor("Sponsor2", "v2", "http://example2.com", "Test Sponsor 2", "test2@example.com")

if GetTotalSponsors() != 2 {
t.Errorf("Expected 2 sponsors, got %d", GetTotalSponsors())
}

// Test the GetList function
s := GetList()
if len(s) != 4 {
t.Errorf("Expected 4 sponsors, got %d", len(s))
}

if s[0] != "Sponsor1" {
t.Errorf("Expected Sponsor1, got %s", s[0])
}
if s[1] != "http://example.com" {
t.Errorf("Expected http://example.com, got %s", s[0])
}
if s[2] != "Sponsor2" {
t.Errorf("Expected Sponsor2, got %s", s[0])
}
if s[3] != "http://example2.com" {
t.Errorf("Expected http://example2.com, got %s", s[0])
}

// Render sponsor by name
rendered := Render("Sponsor1")
expected := "## Sponsor: Sponsor1\n\n * API Version: v1\n * Discover URL: http://example.com\n * Description: Test Sponsor 1\n * Contact Info: test1@example.com\n * Height: " + strconv.FormatInt(std.GetHeight(), 10) + "\n"
if rendered != expected {
t.Errorf("Expected:\n%s\nGot:\n%s", expected, rendered)
}

// Render sponsor by address
rendered = Render(caller.String())
if rendered != expected {
t.Errorf("Expected:\n%s\nGot:\n%s", expected, rendered)
}

// Render home
expectedHome := " * [Sponsor1](/r/demo/sponsors:Sponsor1)\n"
if Render("") != expectedHome {
t.Errorf("Expected:\n%s\nGot:\n%s", expectedHome, Render(""))
}
}
Loading