diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 549a9bb..2a084a3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,6 +10,4 @@ jobs: - uses: aviate-labs/setup-dfx@v0.2.3 with: dfx-version: 0.13.1 - - run: | - make test - make test-ledger + - run: make test diff --git a/Makefile b/Makefile index 18441c1..266c61b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: test test-cover test-ledger gen gen-ic fmt +.PHONY: test test-cover gen gen-ic fmt test: go test -v -cover ./... @@ -7,11 +7,6 @@ test-cover: go test -v -coverprofile=coverage.out ./... go tool cover -html=coverage.out -test-ledger: - cd ic; dfx start --background --clean - cd ic/testdata; dfx deploy --no-wallet - cd ic; DFX=true go test -v icpledger_test.go; dfx stop - gen: cd candid && go generate diff --git a/candid/idl/nat.go b/candid/idl/nat.go index f93913f..1e1df7c 100644 --- a/candid/idl/nat.go +++ b/candid/idl/nat.go @@ -252,7 +252,10 @@ func (n NatType) EncodeValue(v any) ([]byte, error) { if !ok { return nil, fmt.Errorf("invalid value: %v", v) } - return leb128.EncodeUnsigned(v.BigInt()) + if bi := v.BigInt(); bi != nil { + return leb128.EncodeUnsigned(bi) + } + return leb128.EncodeUnsigned(big.NewInt(0)) case 8: v, err := encodeNat64(v) if err != nil { diff --git a/candid/idl/optional.go b/candid/idl/optional.go index 48c2e08..e256f57 100644 --- a/candid/idl/optional.go +++ b/candid/idl/optional.go @@ -70,6 +70,9 @@ func (o OptionalType) EncodeValue(v any) ([]byte, error) { return []byte{0x00}, nil } if v := reflect.ValueOf(v); v.Kind() == reflect.Ptr { + if v.IsNil() { + return []byte{0x00}, nil + } return o.EncodeValue(v.Elem().Interface()) } v_, err := o.Type.EncodeValue(v) diff --git a/candid/idl/record.go b/candid/idl/record.go index c175cce..ab7ab08 100644 --- a/candid/idl/record.go +++ b/candid/idl/record.go @@ -18,6 +18,10 @@ func StructToMap(value any) (map[string]any, error) { m := make(map[string]any) for i := 0; i < v.NumField(); i++ { field := v.Type().Field(i) + if !field.IsExported() { + continue + } + tag := ParseTags(field) m[tag.Name] = v.Field(i).Interface() } @@ -135,6 +139,10 @@ func (record RecordType) String() string { } func (record RecordType) UnmarshalGo(raw any, _v any) error { + if raw == nil && record.Fields == nil { + return nil // Empty record. + } + m := make(map[string]any) switch rv := reflect.ValueOf(raw); rv.Kind() { case reflect.Map: diff --git a/common/cmotoko/result.go b/common/cmotoko/result.go new file mode 100644 index 0000000..cac872b --- /dev/null +++ b/common/cmotoko/result.go @@ -0,0 +1,8 @@ +package cmotoko + +// Result is a generic type that represents either success (Ok) or failure (Err). +// It is used as the return type of functions which may fail. +type Result[T any, E any] struct { + Ok *T `ic:"ok,variant"` + Err *E `ic:"err,variant"` +} diff --git a/common/crust/result.go b/common/crust/result.go new file mode 100644 index 0000000..b4ef18a --- /dev/null +++ b/common/crust/result.go @@ -0,0 +1,8 @@ +package cmotoko + +// Result is a generic type that represents either success (Ok) or failure (Err). +// It is used as the return type of functions which may fail. +type Result[T any, E any] struct { + Ok *T `ic:"Ok,variant"` + Err *E `ic:"Err,variant"` +} diff --git a/gen/generator.go b/gen/generator.go index d3e3fe0..55e9234 100644 --- a/gen/generator.go +++ b/gen/generator.go @@ -22,7 +22,7 @@ var ( templates map[string]*template.Template ) -func funcName(name string) string { +func funcName(prefix, name string) string { if strings.HasPrefix(name, "\"") { name = name[1 : len(name)-1] } @@ -30,6 +30,9 @@ func funcName(name string) string { for _, p := range strings.Split(name, "_") { str += strings.ToUpper(string(p[0])) + p[1:] } + if prefix != "" { + return fmt.Sprintf("%s.%s", prefix, str) + } return str } @@ -66,6 +69,7 @@ func rawName(name string) string { // Generator is a generator for a given service description. type Generator struct { AgentName string + ModulePath string CanisterName string PackageName string ServiceDescription did.Description @@ -92,8 +96,8 @@ func (g *Generator) Generate() ([]byte, error) { switch definition := definition.(type) { case did.Type: definitions = append(definitions, agentArgsDefinition{ - Name: funcName(definition.Id), - Type: g.dataToString(definition.Data), + Name: funcName("", definition.Id), + Type: g.dataToString("", definition.Data), }) } } @@ -114,13 +118,13 @@ func (g *Generator) Generate() ([]byte, error) { } argumentTypes = append(argumentTypes, agentArgsMethodArgument{ Name: n, - Type: g.dataToString(t.Data), + Type: g.dataToString("", t.Data), }) } var returnTypes []string for _, t := range f.ResTypes { - returnTypes = append(returnTypes, g.dataToString(t.Data)) + returnTypes = append(returnTypes, g.dataToString("", t.Data)) } typ := "Call" @@ -130,7 +134,7 @@ func (g *Generator) Generate() ([]byte, error) { methods = append(methods, agentArgsMethod{ RawName: name, - Name: funcName(name), + Name: funcName("", name), Type: typ, ArgumentTypes: argumentTypes, ReturnTypes: returnTypes, @@ -155,16 +159,74 @@ func (g *Generator) Generate() ([]byte, error) { return io.ReadAll(&tmpl) } -func (g *Generator) dataToString(data did.Data) string { +func (g *Generator) GenerateMock() ([]byte, error) { + var methods []agentArgsMethod + for _, service := range g.ServiceDescription.Services { + for _, method := range service.Methods { + name := rawName(method.Name) + f := method.Func + + var argumentTypes []agentArgsMethodArgument + for i, t := range f.ArgTypes { + var n string + if (t.Name != nil) && (*t.Name != "") { + n = *t.Name + } else { + n = fmt.Sprintf("arg%d", i) + } + argumentTypes = append(argumentTypes, agentArgsMethodArgument{ + Name: n, + Type: g.dataToString(g.PackageName, t.Data), + }) + } + + var returnTypes []string + for _, t := range f.ResTypes { + returnTypes = append(returnTypes, g.dataToString(g.PackageName, t.Data)) + } + + typ := "Call" + if f.Annotation != nil && *f.Annotation == did.AnnQuery { + typ = "Query" + } + + methods = append(methods, agentArgsMethod{ + RawName: name, + Name: funcName("", name), + Type: typ, + ArgumentTypes: argumentTypes, + ReturnTypes: returnTypes, + }) + } + } + t, ok := templates["agent_test"] + if !ok { + return nil, fmt.Errorf("template not found") + } + var tmpl bytes.Buffer + if err := t.Execute(&tmpl, agentMockArgs{ + AgentName: g.AgentName, + CanisterName: g.CanisterName, + PackageName: g.PackageName, + ModulePath: g.ModulePath, + UsedIDL: g.usedIDL, + Methods: methods, + }); err != nil { + return nil, err + } + return io.ReadAll(&tmpl) +} + +func (g *Generator) dataToString(prefix string, data did.Data) string { switch t := data.(type) { case did.Blob: return "[]byte" case did.DataId: - return funcName(string(t)) + return funcName(prefix, string(t)) case did.Func: return "struct { /* NOT SUPPORTED */ }" case did.Optional: - return fmt.Sprintf("*%s", g.dataToString(t.Data)) + return fmt.Sprintf("*%s", g.dataToString(prefix, t.Data)) case did.Primitive: switch t { case "nat8", "nat16", "nat32", "nat64": @@ -199,16 +261,16 @@ func (g *Generator) dataToString(data did.Data) string { name := originalName if n := field.Name; n != nil { originalName = *n - name = funcName(*n) + name = funcName("", *n) } if l := len(name); l > sizeName { sizeName = l } var typ string if field.Data != nil { - typ = g.dataToString(*field.Data) + typ = g.dataToString(prefix, *field.Data) } else { - typ = funcName(*field.NameData) + typ = funcName(prefix, *field.NameData) } for _, typ := range strings.Split(typ, "\n") { if l := len(typ); l > sizeType { @@ -257,15 +319,15 @@ func (g *Generator) dataToString(data did.Data) string { typ string }{originalName: name, name: name, typ: "struct{}"}) } else { - name := funcName(*field.Name) + name := funcName("", *field.Name) if l := len(name); l > sizeName { sizeName = l } var typ string if field.Data != nil { - typ = g.dataToString(*field.Data) + typ = g.dataToString(prefix, *field.Data) } else { - typ = funcName(*field.NameData) + typ = funcName(prefix, *field.NameData) } for _, typ := range strings.Split(typ, "\n") { if l := len(typ); l > sizeType { @@ -289,7 +351,7 @@ func (g *Generator) dataToString(data did.Data) string { } return fmt.Sprintf("struct {\n%s}", record) case did.Vector: - return fmt.Sprintf("[]%s", g.dataToString(t.Data)) + return fmt.Sprintf("[]%s", g.dataToString(prefix, t.Data)) default: panic(fmt.Sprintf("unknown type: %T", t)) } @@ -321,3 +383,12 @@ type agentArgsMethodArgument struct { Name string Type string } + +type agentMockArgs struct { + AgentName string + CanisterName string + PackageName string + ModulePath string + UsedIDL bool + Methods []agentArgsMethod +} diff --git a/gen/templates/agent_test.gotmpl b/gen/templates/agent_test.gotmpl new file mode 100644 index 0000000..79c2dcb --- /dev/null +++ b/gen/templates/agent_test.gotmpl @@ -0,0 +1,57 @@ +// Automatically generated by https://github.com/aviate-labs/agent-go. +package {{ .PackageName }}_test + +import ( + "github.com/aviate-labs/agent-go" + {{ if .UsedIDL }}"github.com/aviate-labs/agent-go/candid/idl"{{ end }} + "github.com/aviate-labs/agent-go/mock" + "github.com/aviate-labs/agent-go/principal" + "net/http/httptest" + "net/url" + "testing" + + "{{ .ModulePath }}/{{ .PackageName }}" +) + +// newAgent creates a new agent with the given (mock) methods. +// Runs a mock replica in the background. +func newAgent(methods []mock.Method) (*{{ .PackageName }}.Agent, error) { + replica := mock.NewReplica() + canisterId := principal.Principal{Raw: []byte("{{ .PackageName }}")} + replica.AddCanister(canisterId, methods) + s := httptest.NewServer(replica) + u, _ := url.Parse(s.URL) + a, err := {{ .PackageName }}.NewAgent(canisterId, agent.Config{ + ClientConfig: &agent.ClientConfig{Host: u}, + FetchRootKey: true, + }) + if err != nil { + return nil, err + } + return a, nil +} +{{- range .Methods }} + +// Test_{{ .Name }} tests the "{{ .RawName }}" method on the "{{ $.CanisterName }}" canister. +func Test_{{ .Name }}(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "{{ .RawName }}", + Arguments: []any{{ "{" }}{{ range $i, $e := .ArgumentTypes }}{{ if $i }}, {{ end }}new({{ $e.Type }}){{ end }}{{ "}" }}, + Handler: func (request mock.Request) ([]any, error) { + return []any{{ "{" }}{{ range $i, $e := .ReturnTypes }}{{ if $i }}, {{ end }}*new({{ $e }}){{ end }}{{ "}" }}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + {{ range $i, $e := .ArgumentTypes }} + var a{{ $i }} {{ $e.Type }} + {{- end }} + if {{ range .ReturnTypes }}_, {{ end }}err := a.{{ .Name }}({{ range $i, $e := .ArgumentTypes }}{{ if $i }}, {{ end }}a{{ $i }}{{ end }}); err != nil { + t.Fatal(err) + } + +} +{{- end }} diff --git a/ic/assetstorage/agent_test.go b/ic/assetstorage/agent_test.go new file mode 100755 index 0000000..4509743 --- /dev/null +++ b/ic/assetstorage/agent_test.go @@ -0,0 +1,823 @@ +// Automatically generated by https://github.com/aviate-labs/agent-go. +package assetstorage_test + +import ( + "github.com/aviate-labs/agent-go" + "github.com/aviate-labs/agent-go/candid/idl" + "github.com/aviate-labs/agent-go/mock" + "github.com/aviate-labs/agent-go/principal" + "net/http/httptest" + "net/url" + "testing" + + "github.com/aviate-labs/agent-go/ic/assetstorage" +) + +// Test_ApiVersion tests the "api_version" method on the "assetstorage" canister. +func Test_ApiVersion(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "api_version", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(uint16)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.ApiVersion(); err != nil { + t.Fatal(err) + } + +} + +// Test_Authorize tests the "authorize" method on the "assetstorage" canister. +func Test_Authorize(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "authorize", + Arguments: []any{new(principal.Principal)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 principal.Principal + if err := a.Authorize(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_CertifiedTree tests the "certified_tree" method on the "assetstorage" canister. +func Test_CertifiedTree(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "certified_tree", + Arguments: []any{new(struct { + })}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(struct { + Certificate []byte `ic:"certificate"` + Tree []byte `ic:"tree"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 struct { + } + if _, err := a.CertifiedTree(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_Clear tests the "clear" method on the "assetstorage" canister. +func Test_Clear(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "clear", + Arguments: []any{new(assetstorage.ClearArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.ClearArguments + if err := a.Clear(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_CommitBatch tests the "commit_batch" method on the "assetstorage" canister. +func Test_CommitBatch(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "commit_batch", + Arguments: []any{new(assetstorage.CommitBatchArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.CommitBatchArguments + if err := a.CommitBatch(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_CommitProposedBatch tests the "commit_proposed_batch" method on the "assetstorage" canister. +func Test_CommitProposedBatch(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "commit_proposed_batch", + Arguments: []any{new(assetstorage.CommitProposedBatchArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.CommitProposedBatchArguments + if err := a.CommitProposedBatch(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_ComputeEvidence tests the "compute_evidence" method on the "assetstorage" canister. +func Test_ComputeEvidence(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "compute_evidence", + Arguments: []any{new(assetstorage.ComputeEvidenceArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(*[]byte)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.ComputeEvidenceArguments + if _, err := a.ComputeEvidence(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_CreateAsset tests the "create_asset" method on the "assetstorage" canister. +func Test_CreateAsset(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "create_asset", + Arguments: []any{new(assetstorage.CreateAssetArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.CreateAssetArguments + if err := a.CreateAsset(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_CreateBatch tests the "create_batch" method on the "assetstorage" canister. +func Test_CreateBatch(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "create_batch", + Arguments: []any{new(struct { + })}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(struct { + BatchId assetstorage.BatchId `ic:"batch_id"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 struct { + } + if _, err := a.CreateBatch(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_CreateChunk tests the "create_chunk" method on the "assetstorage" canister. +func Test_CreateChunk(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "create_chunk", + Arguments: []any{new(struct { + BatchId assetstorage.BatchId `ic:"batch_id"` + Content []byte `ic:"content"` + })}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(struct { + ChunkId assetstorage.ChunkId `ic:"chunk_id"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 struct { + BatchId assetstorage.BatchId `ic:"batch_id"` + Content []byte `ic:"content"` + } + if _, err := a.CreateChunk(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_Deauthorize tests the "deauthorize" method on the "assetstorage" canister. +func Test_Deauthorize(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "deauthorize", + Arguments: []any{new(principal.Principal)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 principal.Principal + if err := a.Deauthorize(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_DeleteAsset tests the "delete_asset" method on the "assetstorage" canister. +func Test_DeleteAsset(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "delete_asset", + Arguments: []any{new(assetstorage.DeleteAssetArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.DeleteAssetArguments + if err := a.DeleteAsset(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_DeleteBatch tests the "delete_batch" method on the "assetstorage" canister. +func Test_DeleteBatch(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "delete_batch", + Arguments: []any{new(assetstorage.DeleteBatchArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.DeleteBatchArguments + if err := a.DeleteBatch(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_Get tests the "get" method on the "assetstorage" canister. +func Test_Get(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "get", + Arguments: []any{new(struct { + Key assetstorage.Key `ic:"key"` + AcceptEncodings []string `ic:"accept_encodings"` + })}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(struct { + Content []byte `ic:"content"` + ContentType string `ic:"content_type"` + ContentEncoding string `ic:"content_encoding"` + Sha256 *[]byte `ic:"sha256,omitempty"` + TotalLength idl.Nat `ic:"total_length"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 struct { + Key assetstorage.Key `ic:"key"` + AcceptEncodings []string `ic:"accept_encodings"` + } + if _, err := a.Get(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_GetAssetProperties tests the "get_asset_properties" method on the "assetstorage" canister. +func Test_GetAssetProperties(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "get_asset_properties", + Arguments: []any{new(assetstorage.Key)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(struct { + MaxAge *uint64 `ic:"max_age,omitempty"` + Headers *[]assetstorage.HeaderField `ic:"headers,omitempty"` + AllowRawAccess *bool `ic:"allow_raw_access,omitempty"` + IsAliased *bool `ic:"is_aliased,omitempty"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.Key + if _, err := a.GetAssetProperties(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_GetChunk tests the "get_chunk" method on the "assetstorage" canister. +func Test_GetChunk(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "get_chunk", + Arguments: []any{new(struct { + Key assetstorage.Key `ic:"key"` + ContentEncoding string `ic:"content_encoding"` + Index idl.Nat `ic:"index"` + Sha256 *[]byte `ic:"sha256,omitempty"` + })}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(struct { + Content []byte `ic:"content"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 struct { + Key assetstorage.Key `ic:"key"` + ContentEncoding string `ic:"content_encoding"` + Index idl.Nat `ic:"index"` + Sha256 *[]byte `ic:"sha256,omitempty"` + } + if _, err := a.GetChunk(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_GrantPermission tests the "grant_permission" method on the "assetstorage" canister. +func Test_GrantPermission(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "grant_permission", + Arguments: []any{new(assetstorage.GrantPermission)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.GrantPermission + if err := a.GrantPermission(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_HttpRequest tests the "http_request" method on the "assetstorage" canister. +func Test_HttpRequest(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "http_request", + Arguments: []any{new(assetstorage.HttpRequest)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(assetstorage.HttpResponse)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.HttpRequest + if _, err := a.HttpRequest(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_HttpRequestStreamingCallback tests the "http_request_streaming_callback" method on the "assetstorage" canister. +func Test_HttpRequestStreamingCallback(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "http_request_streaming_callback", + Arguments: []any{new(assetstorage.StreamingCallbackToken)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(*assetstorage.StreamingCallbackHttpResponse)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.StreamingCallbackToken + if _, err := a.HttpRequestStreamingCallback(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_List tests the "list" method on the "assetstorage" canister. +func Test_List(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "list", + Arguments: []any{new(struct { + })}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new([]struct { + Key assetstorage.Key `ic:"key"` + ContentType string `ic:"content_type"` + Encodings []struct { + ContentEncoding string `ic:"content_encoding"` + Sha256 *[]byte `ic:"sha256,omitempty"` + Length idl.Nat `ic:"length"` + Modified assetstorage.Time `ic:"modified"` + } `ic:"encodings"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 struct { + } + if _, err := a.List(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_ListAuthorized tests the "list_authorized" method on the "assetstorage" canister. +func Test_ListAuthorized(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "list_authorized", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new([]principal.Principal)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.ListAuthorized(); err != nil { + t.Fatal(err) + } + +} + +// Test_ListPermitted tests the "list_permitted" method on the "assetstorage" canister. +func Test_ListPermitted(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "list_permitted", + Arguments: []any{new(assetstorage.ListPermitted)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new([]principal.Principal)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.ListPermitted + if _, err := a.ListPermitted(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_ProposeCommitBatch tests the "propose_commit_batch" method on the "assetstorage" canister. +func Test_ProposeCommitBatch(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "propose_commit_batch", + Arguments: []any{new(assetstorage.CommitBatchArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.CommitBatchArguments + if err := a.ProposeCommitBatch(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_RevokePermission tests the "revoke_permission" method on the "assetstorage" canister. +func Test_RevokePermission(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "revoke_permission", + Arguments: []any{new(assetstorage.RevokePermission)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.RevokePermission + if err := a.RevokePermission(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_SetAssetContent tests the "set_asset_content" method on the "assetstorage" canister. +func Test_SetAssetContent(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "set_asset_content", + Arguments: []any{new(assetstorage.SetAssetContentArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.SetAssetContentArguments + if err := a.SetAssetContent(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_SetAssetProperties tests the "set_asset_properties" method on the "assetstorage" canister. +func Test_SetAssetProperties(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "set_asset_properties", + Arguments: []any{new(assetstorage.SetAssetPropertiesArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.SetAssetPropertiesArguments + if err := a.SetAssetProperties(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_Store tests the "store" method on the "assetstorage" canister. +func Test_Store(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "store", + Arguments: []any{new(struct { + Key assetstorage.Key `ic:"key"` + ContentType string `ic:"content_type"` + ContentEncoding string `ic:"content_encoding"` + Content []byte `ic:"content"` + Sha256 *[]byte `ic:"sha256,omitempty"` + })}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 struct { + Key assetstorage.Key `ic:"key"` + ContentType string `ic:"content_type"` + ContentEncoding string `ic:"content_encoding"` + Content []byte `ic:"content"` + Sha256 *[]byte `ic:"sha256,omitempty"` + } + if err := a.Store(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_TakeOwnership tests the "take_ownership" method on the "assetstorage" canister. +func Test_TakeOwnership(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "take_ownership", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if err := a.TakeOwnership(); err != nil { + t.Fatal(err) + } + +} + +// Test_UnsetAssetContent tests the "unset_asset_content" method on the "assetstorage" canister. +func Test_UnsetAssetContent(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "unset_asset_content", + Arguments: []any{new(assetstorage.UnsetAssetContentArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.UnsetAssetContentArguments + if err := a.UnsetAssetContent(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_ValidateCommitProposedBatch tests the "validate_commit_proposed_batch" method on the "assetstorage" canister. +func Test_ValidateCommitProposedBatch(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "validate_commit_proposed_batch", + Arguments: []any{new(assetstorage.CommitProposedBatchArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(assetstorage.ValidationResult)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.CommitProposedBatchArguments + if _, err := a.ValidateCommitProposedBatch(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_ValidateGrantPermission tests the "validate_grant_permission" method on the "assetstorage" canister. +func Test_ValidateGrantPermission(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "validate_grant_permission", + Arguments: []any{new(assetstorage.GrantPermission)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(assetstorage.ValidationResult)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.GrantPermission + if _, err := a.ValidateGrantPermission(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_ValidateRevokePermission tests the "validate_revoke_permission" method on the "assetstorage" canister. +func Test_ValidateRevokePermission(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "validate_revoke_permission", + Arguments: []any{new(assetstorage.RevokePermission)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(assetstorage.ValidationResult)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 assetstorage.RevokePermission + if _, err := a.ValidateRevokePermission(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_ValidateTakeOwnership tests the "validate_take_ownership" method on the "assetstorage" canister. +func Test_ValidateTakeOwnership(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "validate_take_ownership", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(assetstorage.ValidationResult)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.ValidateTakeOwnership(); err != nil { + t.Fatal(err) + } + +} + +// newAgent creates a new agent with the given (mock) methods. +// Runs a mock replica in the background. +func newAgent(methods []mock.Method) (*assetstorage.Agent, error) { + replica := mock.NewReplica() + canisterId := principal.Principal{Raw: []byte("assetstorage")} + replica.AddCanister(canisterId, methods) + s := httptest.NewServer(replica) + u, _ := url.Parse(s.URL) + a, err := assetstorage.NewAgent(canisterId, agent.Config{ + ClientConfig: &agent.ClientConfig{Host: u}, + FetchRootKey: true, + }) + if err != nil { + return nil, err + } + return a, nil +} diff --git a/ic/cmc/agent_test.go b/ic/cmc/agent_test.go new file mode 100755 index 0000000..0d158f6 --- /dev/null +++ b/ic/cmc/agent_test.go @@ -0,0 +1,118 @@ +// Automatically generated by https://github.com/aviate-labs/agent-go. +package cmc_test + +import ( + "github.com/aviate-labs/agent-go" + + "github.com/aviate-labs/agent-go/mock" + "github.com/aviate-labs/agent-go/principal" + "net/http/httptest" + "net/url" + "testing" + + "github.com/aviate-labs/agent-go/ic/cmc" +) + +// Test_GetIcpXdrConversionRate tests the "get_icp_xdr_conversion_rate" method on the "cmc" canister. +func Test_GetIcpXdrConversionRate(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "get_icp_xdr_conversion_rate", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(cmc.IcpXdrConversionRateResponse)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.GetIcpXdrConversionRate(); err != nil { + t.Fatal(err) + } + +} + +// Test_GetSubnetTypesToSubnets tests the "get_subnet_types_to_subnets" method on the "cmc" canister. +func Test_GetSubnetTypesToSubnets(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "get_subnet_types_to_subnets", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(cmc.SubnetTypesToSubnetsResponse)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.GetSubnetTypesToSubnets(); err != nil { + t.Fatal(err) + } + +} + +// Test_NotifyCreateCanister tests the "notify_create_canister" method on the "cmc" canister. +func Test_NotifyCreateCanister(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "notify_create_canister", + Arguments: []any{new(cmc.NotifyCreateCanisterArg)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(cmc.NotifyCreateCanisterResult)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 cmc.NotifyCreateCanisterArg + if _, err := a.NotifyCreateCanister(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_NotifyTopUp tests the "notify_top_up" method on the "cmc" canister. +func Test_NotifyTopUp(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "notify_top_up", + Arguments: []any{new(cmc.NotifyTopUpArg)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(cmc.NotifyTopUpResult)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 cmc.NotifyTopUpArg + if _, err := a.NotifyTopUp(a0); err != nil { + t.Fatal(err) + } + +} + +// newAgent creates a new agent with the given (mock) methods. +// Runs a mock replica in the background. +func newAgent(methods []mock.Method) (*cmc.Agent, error) { + replica := mock.NewReplica() + canisterId := principal.Principal{Raw: []byte("cmc")} + replica.AddCanister(canisterId, methods) + s := httptest.NewServer(replica) + u, _ := url.Parse(s.URL) + a, err := cmc.NewAgent(canisterId, agent.Config{ + ClientConfig: &agent.ClientConfig{Host: u}, + FetchRootKey: true, + }) + if err != nil { + return nil, err + } + return a, nil +} diff --git a/ic/icparchive/agent_test.go b/ic/icparchive/agent_test.go index 2999d5e..021b0fb 100644 --- a/ic/icparchive/agent_test.go +++ b/ic/icparchive/agent_test.go @@ -1,128 +1,54 @@ +// Automatically generated by https://github.com/aviate-labs/agent-go. package icparchive_test import ( - "encoding/hex" - "fmt" "github.com/aviate-labs/agent-go" - "github.com/aviate-labs/agent-go/candid/idl" - "github.com/aviate-labs/agent-go/ic" - "github.com/aviate-labs/agent-go/ic/icparchive" - "github.com/aviate-labs/agent-go/ic/icpledger" + + "github.com/aviate-labs/agent-go/mock" + "github.com/aviate-labs/agent-go/principal" + "net/http/httptest" + "net/url" "testing" - "time" + + "github.com/aviate-labs/agent-go/ic/icparchive" ) -func ExampleAgent_GetBlocks() { - ledger, _ := icpledger.NewAgent(ic.LEDGER_PRINCIPAL, agent.Config{}) - archives, _ := ledger.Archives() - for _, archive := range archives.Archives { - archive, _ := icparchive.NewAgent(archive.CanisterId, agent.Config{}) - blocks, _ := archive.GetBlocks(icparchive.GetBlocksArgs{Start: 0, Length: 1}) - block := blocks.Ok.Blocks[0] - unix := time.Unix(int64(block.Timestamp.TimestampNanos/1_000_000_000), 0) - op := block.Transaction.Operation.Mint - to := hex.EncodeToString(op.To) - amount := op.Amount.E8s / 10_000_000 - fmt.Printf("%s %s %d ICP\n", unix.UTC().String(), to, amount) +// Test_GetBlocks tests the "get_blocks" method on the "icparchive" canister. +func Test_GetBlocks(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "get_blocks", + Arguments: []any{new(icparchive.GetBlocksArgs)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(icparchive.GetBlocksResult)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 icparchive.GetBlocksArgs + if _, err := a.GetBlocks(a0); err != nil { + t.Fatal(err) } - // Output: - // 2021-05-06 19:17:10 +0000 UTC 529ea51c22e8d66e8302eabd9297b100fdb369109822248bb86939a671fbc55b 15431 ICP + } -func TestUnmarshal_operation(t *testing.T) { - t.Run("Mint", func(t *testing.T) { - var o icparchive.Operation - data, _ := hex.DecodeString("4449444c046b01c2f5d59903016c02fbca0102d8a38ca80d036d7b6c01e0a9b3027801000004746573741027000000000000") - if err := idl.Unmarshal(data, []any{&o}); err != nil { - t.Fatal(err) - } - if o.Mint == nil { - t.Fatal(o.Mint) - } - if o.Burn != nil && o.Transfer != nil && o.Approve == nil && o.TransferFrom != nil { - t.Fatal(o) - } - op := o.Mint - if string(op.To) != "test" { - t.Error(op.To) - } - if op.Amount.E8s != 10_000 { - t.Error(op.Amount) - } - }) - t.Run("Burn", func(t *testing.T) { - var o icparchive.Operation - data, _ := hex.DecodeString("4449444c046b01ef80e5df02016c02eaca8a9e0402d8a38ca80d036d7b6c01e0a9b3027801000004746573741027000000000000") - if err := idl.Unmarshal(data, []any{&o}); err != nil { - t.Fatal(err) - } - if o.Burn == nil { - t.Fatal(o.Burn) - } - if o.Mint != nil && o.Transfer != nil && o.Approve == nil && o.TransferFrom != nil { - t.Fatal(o) - } - op := o.Burn - if string(op.From) != "test" { - t.Error(op.From) - } - if op.Amount.E8s != 10_000 { - t.Error(op.Amount) - } - }) - t.Run("Transfer", func(t *testing.T) { - var o icparchive.Operation - data, _ := hex.DecodeString("4449444c046b01cbd6fda00b016c04fbca0102c6fcb60203eaca8a9e0402d8a38ca80d036d7b6c01e0a9b302780100000474657374000000000000000004746573741027000000000000") - if err := idl.Unmarshal(data, []any{&o}); err != nil { - t.Fatal(err) - } - if o.Transfer == nil { - t.Fatal(o.Transfer) - } - if o.Transfer != nil && o.Burn != nil && o.Approve == nil && o.TransferFrom != nil { - t.Fatal(o) - } - op := o.Transfer - if string(op.From) != "test" { - t.Error(op.From) - } - if string(op.To) != "test" { - t.Error(op.To) - } - if op.Amount.E8s != 10_000 { - t.Error(op.Amount) - } - if op.Fee.E8s != 0 { - t.Error(op.Fee) - } - }) - t.Run("Approve", func(t *testing.T) { - var o icparchive.Operation - data, _ := hex.DecodeString("4449444c046b01adfaedfb01016c05c6fcb60202eaca8a9e0403b98792ea077cdea7f7da0d7fcb96dcb40e036c01e0a9b302786d7b0100000000000000000000047465737490ce000474657374") - if err := idl.Unmarshal(data, []any{&o}); err != nil { - t.Fatal(err) - } - if o.Approve == nil { - t.Fatal(o.Transfer) - } - if o.Mint != nil && o.Burn != nil && o.Transfer == nil && o.TransferFrom != nil { - t.Fatal(o) - } - op := o.Approve - if string(op.From) != "test" { - t.Error(op.From) - } - if string(op.Spender) != "test" { - t.Error(op.Spender) - } - if op.AllowanceE8s.BigInt().Int64() != 10_000 { - t.Error(op.AllowanceE8s) - } - if op.Fee.E8s != 0 { - t.Error(op.Fee) - } - if op.ExpiresAt != nil { - t.Error(op.ExpiresAt) - } +// newAgent creates a new agent with the given (mock) methods. +// Runs a mock replica in the background. +func newAgent(methods []mock.Method) (*icparchive.Agent, error) { + replica := mock.NewReplica() + canisterId := principal.Principal{Raw: []byte("icparchive")} + replica.AddCanister(canisterId, methods) + s := httptest.NewServer(replica) + u, _ := url.Parse(s.URL) + a, err := icparchive.NewAgent(canisterId, agent.Config{ + ClientConfig: &agent.ClientConfig{Host: u}, + FetchRootKey: true, }) + if err != nil { + return nil, err + } + return a, nil } diff --git a/ic/icpledger/agent_test.go b/ic/icpledger/agent_test.go index 5ae3658..bef2aaa 100644 --- a/ic/icpledger/agent_test.go +++ b/ic/icpledger/agent_test.go @@ -1,16 +1,210 @@ +// Automatically generated by https://github.com/aviate-labs/agent-go. package icpledger_test import ( - "fmt" "github.com/aviate-labs/agent-go" - "github.com/aviate-labs/agent-go/ic" + + "github.com/aviate-labs/agent-go/mock" + "github.com/aviate-labs/agent-go/principal" + "net/http/httptest" + "net/url" + "testing" + "github.com/aviate-labs/agent-go/ic/icpledger" ) -func ExampleAgent_Archives() { - a, _ := icpledger.NewAgent(ic.LEDGER_PRINCIPAL, agent.Config{}) - archives, _ := a.Archives() - fmt.Println(archives) - // Output: - // &{[{qjdve-lqaaa-aaaaa-aaaeq-cai}]} +// Test_AccountBalance tests the "account_balance" method on the "icpledger" canister. +func Test_AccountBalance(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "account_balance", + Arguments: []any{new(icpledger.AccountBalanceArgs)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(icpledger.Tokens)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 icpledger.AccountBalanceArgs + if _, err := a.AccountBalance(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_Archives tests the "archives" method on the "icpledger" canister. +func Test_Archives(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "archives", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(icpledger.Archives)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.Archives(); err != nil { + t.Fatal(err) + } + +} + +// Test_Decimals tests the "decimals" method on the "icpledger" canister. +func Test_Decimals(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "decimals", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(struct { + Decimals uint32 `ic:"decimals"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.Decimals(); err != nil { + t.Fatal(err) + } + +} + +// Test_Name tests the "name" method on the "icpledger" canister. +func Test_Name(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "name", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(struct { + Name string `ic:"name"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.Name(); err != nil { + t.Fatal(err) + } + +} + +// Test_QueryBlocks tests the "query_blocks" method on the "icpledger" canister. +func Test_QueryBlocks(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "query_blocks", + Arguments: []any{new(icpledger.GetBlocksArgs)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(icpledger.QueryBlocksResponse)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 icpledger.GetBlocksArgs + if _, err := a.QueryBlocks(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_Symbol tests the "symbol" method on the "icpledger" canister. +func Test_Symbol(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "symbol", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(struct { + Symbol string `ic:"symbol"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.Symbol(); err != nil { + t.Fatal(err) + } + +} + +// Test_Transfer tests the "transfer" method on the "icpledger" canister. +func Test_Transfer(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "transfer", + Arguments: []any{new(icpledger.TransferArgs)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(icpledger.TransferResult)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 icpledger.TransferArgs + if _, err := a.Transfer(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_TransferFee tests the "transfer_fee" method on the "icpledger" canister. +func Test_TransferFee(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "transfer_fee", + Arguments: []any{new(icpledger.TransferFeeArg)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(icpledger.TransferFee)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 icpledger.TransferFeeArg + if _, err := a.TransferFee(a0); err != nil { + t.Fatal(err) + } + +} + +// newAgent creates a new agent with the given (mock) methods. +// Runs a mock replica in the background. +func newAgent(methods []mock.Method) (*icpledger.Agent, error) { + replica := mock.NewReplica() + canisterId := principal.Principal{Raw: []byte("icpledger")} + replica.AddCanister(canisterId, methods) + s := httptest.NewServer(replica) + u, _ := url.Parse(s.URL) + a, err := icpledger.NewAgent(canisterId, agent.Config{ + ClientConfig: &agent.ClientConfig{Host: u}, + FetchRootKey: true, + }) + if err != nil { + return nil, err + } + return a, nil } diff --git a/ic/icpledger_test.go b/ic/icpledger_test.go deleted file mode 100644 index 141be01..0000000 --- a/ic/icpledger_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package ic_test - -import ( - "encoding/json" - "fmt" - "github.com/aviate-labs/agent-go" - "github.com/aviate-labs/agent-go/ic" - "github.com/aviate-labs/agent-go/ic/icpledger" - "github.com/aviate-labs/agent-go/identity" - "github.com/aviate-labs/agent-go/principal" - "net/url" - "os" - "testing" - "time" -) - -var ( - canisterId principal.Principal - hostRaw = "http://localhost:8000" - host, _ = url.Parse(hostRaw) -) - -func Example_accountBalance() { - host, _ := url.Parse("https://icp0.io") - a, _ := icpledger.NewAgent(ic.LEDGER_PRINCIPAL, agent.Config{ - ClientConfig: &agent.ClientConfig{Host: host}, - FetchRootKey: true, - }) - name, _ := a.Name() - fmt.Println(name.Name) - // Output: - // Internet Computer -} - -func TestAgent(t *testing.T) { - if os.Getenv("DFX") != "true" { - t.SkipNow() - } - - // Default account of the anonymous principal. - defaultAccount := principal.AnonymousID.AccountIdentifier(principal.DefaultSubAccount) - - t.Run("account_balance ed25519", func(t *testing.T) { - id, _ := identity.NewRandomEd25519Identity() - a, _ := icpledger.NewAgent(canisterId, agent.Config{ - Identity: id, - ClientConfig: &agent.ClientConfig{ - Host: host, - }, - FetchRootKey: true, - }) - tokens, err := a.AccountBalance(icpledger.AccountBalanceArgs{ - Account: defaultAccount[:], - }) - if err != nil { - t.Fatal(err) - } - if tokens.E8s != 1 { - t.Error(tokens) - } - }) - - t.Run("account_balance secp256k1", func(t *testing.T) { - id, _ := identity.NewRandomSecp256k1Identity() - a, _ := icpledger.NewAgent(canisterId, agent.Config{ - Identity: id, - ClientConfig: &agent.ClientConfig{ - Host: host, - }, - FetchRootKey: true, - }) - tokens, err := a.AccountBalance(icpledger.AccountBalanceArgs{ - Account: defaultAccount[:], - }) - if err != nil { - t.Fatal(err) - } - if tokens.E8s != 1 { - t.Error(tokens) - } - }) - - a, _ := icpledger.NewAgent(canisterId, agent.Config{ - ClientConfig: &agent.ClientConfig{ - Host: host, - }, - FetchRootKey: true, - }) - t.Run("account_balance", func(t *testing.T) { - tokens, err := a.AccountBalance(icpledger.AccountBalanceArgs{ - Account: defaultAccount[:], - }) - if err != nil { - t.Fatal(err) - } - if tokens.E8s != 1 { - t.Error(tokens) - } - }) - - t.Run("transfer", func(t *testing.T) { - p, _ := principal.Decode("aaaaa-aa") - subAccount := principal.DefaultSubAccount[:] - to := p.AccountIdentifier(principal.DefaultSubAccount) - result, err := a.Transfer(icpledger.TransferArgs{ - Memo: 0, - Amount: icpledger.Tokens{ - E8s: 100_000, - }, - Fee: icpledger.Tokens{ - E8s: 10_000, - }, - FromSubaccount: &subAccount, - To: to[:], - CreatedAtTime: &icpledger.TimeStamp{ - TimestampNanos: uint64(time.Now().UnixNano()), - }, - }) - if err != nil { - t.Fatal(err) - } - if *result.Ok != 1 { - t.Error(result) - } - }) -} - -func init() { - canisterIdsRaw, _ := os.ReadFile("testdata/.dfx/local/canister_ids.json") - type CanisterIds struct { - Example struct { - IC string `json:"local"` - } `json:"example"` - } - var canisterIds CanisterIds - _ = json.Unmarshal(canisterIdsRaw, &canisterIds) - canisterId, _ = principal.Decode(canisterIds.Example.IC) -} diff --git a/ic/icrc1/agent_test.go b/ic/icrc1/agent_test.go new file mode 100755 index 0000000..99983a6 --- /dev/null +++ b/ic/icrc1/agent_test.go @@ -0,0 +1,253 @@ +// Automatically generated by https://github.com/aviate-labs/agent-go. +package icrc1_test + +import ( + "github.com/aviate-labs/agent-go" + "github.com/aviate-labs/agent-go/candid/idl" + "github.com/aviate-labs/agent-go/mock" + "github.com/aviate-labs/agent-go/principal" + "net/http/httptest" + "net/url" + "testing" + + "github.com/aviate-labs/agent-go/ic/icrc1" +) + +// Test_Icrc1BalanceOf tests the "icrc1_balance_of" method on the "icrc1" canister. +func Test_Icrc1BalanceOf(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "icrc1_balance_of", + Arguments: []any{new(icrc1.Account)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(idl.Nat)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 icrc1.Account + if _, err := a.Icrc1BalanceOf(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_Icrc1Decimals tests the "icrc1_decimals" method on the "icrc1" canister. +func Test_Icrc1Decimals(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "icrc1_decimals", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(uint8)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.Icrc1Decimals(); err != nil { + t.Fatal(err) + } + +} + +// Test_Icrc1Fee tests the "icrc1_fee" method on the "icrc1" canister. +func Test_Icrc1Fee(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "icrc1_fee", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(idl.Nat)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.Icrc1Fee(); err != nil { + t.Fatal(err) + } + +} + +// Test_Icrc1Metadata tests the "icrc1_metadata" method on the "icrc1" canister. +func Test_Icrc1Metadata(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "icrc1_metadata", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new([]struct { + field0 string `ic:"field0"` + field1 icrc1.Value `ic:"field1"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.Icrc1Metadata(); err != nil { + t.Fatal(err) + } + +} + +// Test_Icrc1MintingAccount tests the "icrc1_minting_account" method on the "icrc1" canister. +func Test_Icrc1MintingAccount(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "icrc1_minting_account", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(*icrc1.Account)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.Icrc1MintingAccount(); err != nil { + t.Fatal(err) + } + +} + +// Test_Icrc1Name tests the "icrc1_name" method on the "icrc1" canister. +func Test_Icrc1Name(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "icrc1_name", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(string)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.Icrc1Name(); err != nil { + t.Fatal(err) + } + +} + +// Test_Icrc1SupportedStandards tests the "icrc1_supported_standards" method on the "icrc1" canister. +func Test_Icrc1SupportedStandards(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "icrc1_supported_standards", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new([]struct { + Name string `ic:"name"` + Url string `ic:"url"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.Icrc1SupportedStandards(); err != nil { + t.Fatal(err) + } + +} + +// Test_Icrc1Symbol tests the "icrc1_symbol" method on the "icrc1" canister. +func Test_Icrc1Symbol(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "icrc1_symbol", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(string)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.Icrc1Symbol(); err != nil { + t.Fatal(err) + } + +} + +// Test_Icrc1TotalSupply tests the "icrc1_total_supply" method on the "icrc1" canister. +func Test_Icrc1TotalSupply(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "icrc1_total_supply", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(idl.Nat)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.Icrc1TotalSupply(); err != nil { + t.Fatal(err) + } + +} + +// Test_Icrc1Transfer tests the "icrc1_transfer" method on the "icrc1" canister. +func Test_Icrc1Transfer(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "icrc1_transfer", + Arguments: []any{new(icrc1.TransferArgs)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(struct { + Ok *idl.Nat `ic:"Ok,variant"` + Err *icrc1.TransferError `ic:"Err,variant"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 icrc1.TransferArgs + if _, err := a.Icrc1Transfer(a0); err != nil { + t.Fatal(err) + } + +} + +// newAgent creates a new agent with the given (mock) methods. +// Runs a mock replica in the background. +func newAgent(methods []mock.Method) (*icrc1.Agent, error) { + replica := mock.NewReplica() + canisterId := principal.Principal{Raw: []byte("icrc1")} + replica.AddCanister(canisterId, methods) + s := httptest.NewServer(replica) + u, _ := url.Parse(s.URL) + a, err := icrc1.NewAgent(canisterId, agent.Config{ + ClientConfig: &agent.ClientConfig{Host: u}, + FetchRootKey: true, + }) + if err != nil { + return nil, err + } + return a, nil +} diff --git a/ic/testdata/.gitignore b/ic/testdata/.gitignore deleted file mode 100644 index b0d5185..0000000 --- a/ic/testdata/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.dfx diff --git a/ic/testdata/dfx.json b/ic/testdata/dfx.json deleted file mode 100644 index 248b754..0000000 --- a/ic/testdata/dfx.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "canisters": { - "example": { - "main": "main.mo", - "type": "motoko" - } - } -} diff --git a/ic/testdata/gen.go b/ic/testdata/gen.go index 46085b9..fc8c4e4 100644 --- a/ic/testdata/gen.go +++ b/ic/testdata/gen.go @@ -39,15 +39,29 @@ func main() { _ = os.Mkdir(dir, os.ModePerm) } - g, err := gen.NewGenerator("", name, name, did) - if err != nil { - log.Panic(err) + { + g, err := gen.NewGenerator("", name, name, did) + if err != nil { + log.Panic(err) + } + raw, err := g.Generate() + if err != nil { + log.Panic(err) + } + _ = os.WriteFile(fmt.Sprintf("%s/agent.go", dir), raw, os.ModePerm) } - raw, err := g.Generate() - if err != nil { - log.Panic(err) + { + g, err := gen.NewGenerator("", name, name, did) + g.ModulePath = "github.com/aviate-labs/agent-go/ic" + if err != nil { + log.Panic(err) + } + raw, err := g.GenerateMock() + if err != nil { + log.Panic(err) + } + _ = os.WriteFile(fmt.Sprintf("%s/agent_test.go", dir), raw, os.ModePerm) } - _ = os.WriteFile(fmt.Sprintf("%s/agent.go", dir), raw, os.ModePerm) } } } diff --git a/ic/testdata/ledger.did b/ic/testdata/ledger.did deleted file mode 100644 index 30957f2..0000000 --- a/ic/testdata/ledger.did +++ /dev/null @@ -1,267 +0,0 @@ -// SOURCE: https://raw.githubusercontent.com/dfinity/ic/162f2770e8eac0f98824dc24e663538df8e950e0/rs/rosetta-api/icp_ledger/ledger.did -// This is the official Ledger interface that is guaranteed to be backward compatible. - -// Amount of tokens, measured in 10^-8 of a token. -type Tokens = record { - e8s : nat64; -}; - -// Number of nanoseconds from the UNIX epoch in UTC timezone. -type TimeStamp = record { - timestamp_nanos: nat64; -}; - -// AccountIdentifier is a 32-byte array. -// The first 4 bytes is big-endian encoding of a CRC32 checksum of the last 28 bytes. -type AccountIdentifier = blob; - -// Subaccount is an arbitrary 32-byte byte array. -// Ledger uses subaccounts to compute the source address, which enables one -// principal to control multiple ledger accounts. -type SubAccount = blob; - -// Sequence number of a block produced by the ledger. -type BlockIndex = nat64; - -type Transaction = record { - memo : Memo; - icrc1_memo: opt blob; - operation : opt Operation; - created_at_time : TimeStamp; -}; - -// An arbitrary number associated with a transaction. -// The caller can set it in a `transfer` call as a correlation identifier. -type Memo = nat64; - -// Arguments for the `transfer` call. -type TransferArgs = record { - // Transaction memo. - // See comments for the `Memo` type. - memo: Memo; - // The amount that the caller wants to transfer to the destination address. - amount: Tokens; - // The amount that the caller pays for the transaction. - // Must be 10000 e8s. - fee: Tokens; - // The subaccount from which the caller wants to transfer funds. - // If null, the ledger uses the default (all zeros) subaccount to compute the source address. - // See comments for the `SubAccount` type. - from_subaccount: opt SubAccount; - // The destination account. - // If the transfer is successful, the balance of this address increases by `amount`. - to: AccountIdentifier; - // The point in time when the caller created this request. - // If null, the ledger uses current IC time as the timestamp. - created_at_time: opt TimeStamp; -}; - -type TransferError = variant { - // The fee that the caller specified in the transfer request was not the one that ledger expects. - // The caller can change the transfer fee to the `expected_fee` and retry the request. - BadFee : record { expected_fee : Tokens; }; - // The account specified by the caller doesn't have enough funds. - InsufficientFunds : record { balance: Tokens; }; - // The request is too old. - // The ledger only accepts requests created within 24 hours window. - // This is a non-recoverable error. - TxTooOld : record { allowed_window_nanos: nat64 }; - // The caller specified `created_at_time` that is too far in future. - // The caller can retry the request later. - TxCreatedInFuture : null; - // The ledger has already executed the request. - // `duplicate_of` field is equal to the index of the block containing the original transaction. - TxDuplicate : record { duplicate_of: BlockIndex; } -}; - -type TransferResult = variant { - Ok : BlockIndex; - Err : TransferError; -}; - -// Arguments for the `account_balance` call. -type AccountBalanceArgs = record { - account: AccountIdentifier; -}; - -type TransferFeeArg = record {}; - -type TransferFee = record { - // The fee to pay to perform a transfer - transfer_fee: Tokens; -}; - -type GetBlocksArgs = record { - // The index of the first block to fetch. - start : BlockIndex; - // Max number of blocks to fetch. - length : nat64; -}; - -type Operation = variant { - Mint : record { - to : AccountIdentifier; - amount : Tokens; - }; - Burn : record { - from : AccountIdentifier; - amount : Tokens; - }; - Transfer : record { - from : AccountIdentifier; - to : AccountIdentifier; - amount : Tokens; - fee : Tokens; - }; - Approve : record { - from : AccountIdentifier; - spender : AccountIdentifier; - allowance_e8s : int; - fee : Tokens; - expires_at : opt TimeStamp; - }; - TransferFrom : record { - from : AccountIdentifier; - to : AccountIdentifier; - spender : AccountIdentifier; - amount : Tokens; - fee : Tokens; - }; -}; - - - -type Block = record { - parent_hash : opt blob; - transaction : Transaction; - timestamp : TimeStamp; -}; - -// A prefix of the block range specified in the [GetBlocksArgs] request. -type BlockRange = record { - // A prefix of the requested block range. - // The index of the first block is equal to [GetBlocksArgs.from]. - // - // Note that the number of blocks might be less than the requested - // [GetBlocksArgs.len] for various reasons, for example: - // - // 1. The query might have hit the replica with an outdated state - // that doesn't have the full block range yet. - // 2. The requested range is too large to fit into a single reply. - // - // NOTE: the list of blocks can be empty if: - // 1. [GetBlocksArgs.len] was zero. - // 2. [GetBlocksArgs.from] was larger than the last block known to the canister. - blocks : vec Block; -}; - -// An error indicating that the arguments passed to [QueryArchiveFn] were invalid. -type QueryArchiveError = variant { - // [GetBlocksArgs.from] argument was smaller than the first block - // served by the canister that received the request. - BadFirstBlockIndex : record { - requested_index : BlockIndex; - first_valid_index : BlockIndex; - }; - - // Reserved for future use. - Other : record { - error_code : nat64; - error_message : text; - }; -}; - -type QueryArchiveResult = variant { - // Successfully fetched zero or more blocks. - Ok : BlockRange; - // The [GetBlocksArgs] request was invalid. - Err : QueryArchiveError; -}; - -// A function that is used for fetching archived ledger blocks. -type QueryArchiveFn = func (GetBlocksArgs) -> (QueryArchiveResult) query; - -// The result of a "query_blocks" call. -// -// The structure of the result is somewhat complicated because the main ledger canister might -// not have all the blocks that the caller requested: One or more "archive" canisters might -// store some of the requested blocks. -// -// Note: as of Q4 2021 when this interface is authored, the IC doesn't support making nested -// query calls within a query call. -type QueryBlocksResponse = record { - // The total number of blocks in the chain. - // If the chain length is positive, the index of the last block is `chain_len - 1`. - chain_length : nat64; - - // System certificate for the hash of the latest block in the chain. - // Only present if `query_blocks` is called in a non-replicated query context. - certificate : opt blob; - - // List of blocks that were available in the ledger when it processed the call. - // - // The blocks form a contiguous range, with the first block having index - // [first_block_index] (see below), and the last block having index - // [first_block_index] + len(blocks) - 1. - // - // The block range can be an arbitrary sub-range of the originally requested range. - blocks : vec Block; - - // The index of the first block in "blocks". - // If the blocks vector is empty, the exact value of this field is not specified. - first_block_index : BlockIndex; - - // Encoding of instructions for fetching archived blocks whose indices fall into the - // requested range. - // - // For each entry `e` in [archived_blocks], `[e.from, e.from + len)` is a sub-range - // of the originally requested block range. - archived_blocks : vec record { - // The index of the first archived block that can be fetched using the callback. - start : BlockIndex; - - // The number of blocks that can be fetch using the callback. - length : nat64; - - // The function that should be called to fetch the archived blocks. - // The range of the blocks accessible using this function is given by [from] - // and [len] fields above. - callback : QueryArchiveFn; - }; -}; - -type Archive = record { - canister_id: principal; -}; - -type Archives = record { - archives: vec Archive; -}; - -service : { - // Transfers tokens from a subaccount of the caller to the destination address. - // The source address is computed from the principal of the caller and the specified subaccount. - // When successful, returns the index of the block containing the transaction. - transfer : (TransferArgs) -> (TransferResult); - - // Returns the amount of Tokens on the specified account. - account_balance : (AccountBalanceArgs) -> (Tokens) query; - - // Returns the current transfer_fee. - transfer_fee : (TransferFeeArg) -> (TransferFee) query; - - // Queries blocks in the specified range. - query_blocks : (GetBlocksArgs) -> (QueryBlocksResponse) query; - - // Returns token symbol. - symbol : () -> (record { symbol: text }) query; - - // Returns token name. - name : () -> (record { name: text }) query; - - // Returns token decimals. - decimals : () -> (record { decimals: nat32 }) query; - - // Returns the existing archive canisters information. - archives : () -> (Archives) query; -} \ No newline at end of file diff --git a/ic/testdata/main.mo b/ic/testdata/main.mo deleted file mode 100644 index 8d7da9c..0000000 --- a/ic/testdata/main.mo +++ /dev/null @@ -1,49 +0,0 @@ -actor { - public type Tokens = { - e8s : Nat64; - }; - - public type AccountIdentifier = Blob; - - public type AccountBalanceArgs = { - account : AccountIdentifier; - }; - - public query func account_balance(args : AccountBalanceArgs) : async Tokens { - { e8s = 1 }; - }; - - type TimeStamp = { - timestamp_nanos: Nat64; -}; - - type Memo = Nat64; - - type SubAccount = Blob; - - type BlockIndex = Nat64; - - type TransferArgs = { - memo : Memo; - amount : Tokens; - fee : Tokens; - from_subaccount : ?SubAccount; - to : AccountIdentifier; - created_at_time : ?TimeStamp; - }; - - type TransferError = { - #BadFee : { expected_fee : Tokens; }; - #InsufficientFunds : { balance: Tokens; }; - #TxTooOld : { allowed_window_nanos: Nat64 }; - #TxCreatedInFuture; - #TxDuplicate : { duplicate_of: BlockIndex; } -}; - - type TransferResult = { - #Ok : BlockIndex; - #Err : TransferError; -}; - - public shared func transfer(args : TransferArgs) : async TransferResult { #Ok(1) }; -}; diff --git a/ic/wallet/agent_test.go b/ic/wallet/agent_test.go new file mode 100755 index 0000000..e91d8a9 --- /dev/null +++ b/ic/wallet/agent_test.go @@ -0,0 +1,823 @@ +// Automatically generated by https://github.com/aviate-labs/agent-go. +package wallet_test + +import ( + "github.com/aviate-labs/agent-go" + "github.com/aviate-labs/agent-go/candid/idl" + "github.com/aviate-labs/agent-go/mock" + "github.com/aviate-labs/agent-go/principal" + "net/http/httptest" + "net/url" + "testing" + + "github.com/aviate-labs/agent-go/ic/wallet" +) + +// Test_ApiVersion tests the "api_version" method on the "wallet" canister. +func Test_ApiVersion(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "api_version", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(uint16)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.ApiVersion(); err != nil { + t.Fatal(err) + } + +} + +// Test_Authorize tests the "authorize" method on the "wallet" canister. +func Test_Authorize(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "authorize", + Arguments: []any{new(principal.Principal)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 principal.Principal + if err := a.Authorize(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_CertifiedTree tests the "certified_tree" method on the "wallet" canister. +func Test_CertifiedTree(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "certified_tree", + Arguments: []any{new(struct { + })}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(struct { + Certificate []byte `ic:"certificate"` + Tree []byte `ic:"tree"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 struct { + } + if _, err := a.CertifiedTree(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_Clear tests the "clear" method on the "wallet" canister. +func Test_Clear(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "clear", + Arguments: []any{new(wallet.ClearArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.ClearArguments + if err := a.Clear(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_CommitBatch tests the "commit_batch" method on the "wallet" canister. +func Test_CommitBatch(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "commit_batch", + Arguments: []any{new(wallet.CommitBatchArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.CommitBatchArguments + if err := a.CommitBatch(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_CommitProposedBatch tests the "commit_proposed_batch" method on the "wallet" canister. +func Test_CommitProposedBatch(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "commit_proposed_batch", + Arguments: []any{new(wallet.CommitProposedBatchArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.CommitProposedBatchArguments + if err := a.CommitProposedBatch(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_ComputeEvidence tests the "compute_evidence" method on the "wallet" canister. +func Test_ComputeEvidence(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "compute_evidence", + Arguments: []any{new(wallet.ComputeEvidenceArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(*[]byte)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.ComputeEvidenceArguments + if _, err := a.ComputeEvidence(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_CreateAsset tests the "create_asset" method on the "wallet" canister. +func Test_CreateAsset(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "create_asset", + Arguments: []any{new(wallet.CreateAssetArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.CreateAssetArguments + if err := a.CreateAsset(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_CreateBatch tests the "create_batch" method on the "wallet" canister. +func Test_CreateBatch(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "create_batch", + Arguments: []any{new(struct { + })}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(struct { + BatchId wallet.BatchId `ic:"batch_id"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 struct { + } + if _, err := a.CreateBatch(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_CreateChunk tests the "create_chunk" method on the "wallet" canister. +func Test_CreateChunk(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "create_chunk", + Arguments: []any{new(struct { + BatchId wallet.BatchId `ic:"batch_id"` + Content []byte `ic:"content"` + })}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(struct { + ChunkId wallet.ChunkId `ic:"chunk_id"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 struct { + BatchId wallet.BatchId `ic:"batch_id"` + Content []byte `ic:"content"` + } + if _, err := a.CreateChunk(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_Deauthorize tests the "deauthorize" method on the "wallet" canister. +func Test_Deauthorize(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "deauthorize", + Arguments: []any{new(principal.Principal)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 principal.Principal + if err := a.Deauthorize(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_DeleteAsset tests the "delete_asset" method on the "wallet" canister. +func Test_DeleteAsset(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "delete_asset", + Arguments: []any{new(wallet.DeleteAssetArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.DeleteAssetArguments + if err := a.DeleteAsset(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_DeleteBatch tests the "delete_batch" method on the "wallet" canister. +func Test_DeleteBatch(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "delete_batch", + Arguments: []any{new(wallet.DeleteBatchArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.DeleteBatchArguments + if err := a.DeleteBatch(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_Get tests the "get" method on the "wallet" canister. +func Test_Get(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "get", + Arguments: []any{new(struct { + Key wallet.Key `ic:"key"` + AcceptEncodings []string `ic:"accept_encodings"` + })}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(struct { + Content []byte `ic:"content"` + ContentType string `ic:"content_type"` + ContentEncoding string `ic:"content_encoding"` + Sha256 *[]byte `ic:"sha256,omitempty"` + TotalLength idl.Nat `ic:"total_length"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 struct { + Key wallet.Key `ic:"key"` + AcceptEncodings []string `ic:"accept_encodings"` + } + if _, err := a.Get(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_GetAssetProperties tests the "get_asset_properties" method on the "wallet" canister. +func Test_GetAssetProperties(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "get_asset_properties", + Arguments: []any{new(wallet.Key)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(struct { + MaxAge *uint64 `ic:"max_age,omitempty"` + Headers *[]wallet.HeaderField `ic:"headers,omitempty"` + AllowRawAccess *bool `ic:"allow_raw_access,omitempty"` + IsAliased *bool `ic:"is_aliased,omitempty"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.Key + if _, err := a.GetAssetProperties(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_GetChunk tests the "get_chunk" method on the "wallet" canister. +func Test_GetChunk(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "get_chunk", + Arguments: []any{new(struct { + Key wallet.Key `ic:"key"` + ContentEncoding string `ic:"content_encoding"` + Index idl.Nat `ic:"index"` + Sha256 *[]byte `ic:"sha256,omitempty"` + })}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(struct { + Content []byte `ic:"content"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 struct { + Key wallet.Key `ic:"key"` + ContentEncoding string `ic:"content_encoding"` + Index idl.Nat `ic:"index"` + Sha256 *[]byte `ic:"sha256,omitempty"` + } + if _, err := a.GetChunk(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_GrantPermission tests the "grant_permission" method on the "wallet" canister. +func Test_GrantPermission(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "grant_permission", + Arguments: []any{new(wallet.GrantPermission)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.GrantPermission + if err := a.GrantPermission(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_HttpRequest tests the "http_request" method on the "wallet" canister. +func Test_HttpRequest(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "http_request", + Arguments: []any{new(wallet.HttpRequest)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(wallet.HttpResponse)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.HttpRequest + if _, err := a.HttpRequest(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_HttpRequestStreamingCallback tests the "http_request_streaming_callback" method on the "wallet" canister. +func Test_HttpRequestStreamingCallback(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "http_request_streaming_callback", + Arguments: []any{new(wallet.StreamingCallbackToken)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(*wallet.StreamingCallbackHttpResponse)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.StreamingCallbackToken + if _, err := a.HttpRequestStreamingCallback(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_List tests the "list" method on the "wallet" canister. +func Test_List(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "list", + Arguments: []any{new(struct { + })}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new([]struct { + Key wallet.Key `ic:"key"` + ContentType string `ic:"content_type"` + Encodings []struct { + ContentEncoding string `ic:"content_encoding"` + Sha256 *[]byte `ic:"sha256,omitempty"` + Length idl.Nat `ic:"length"` + Modified wallet.Time `ic:"modified"` + } `ic:"encodings"` + })}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 struct { + } + if _, err := a.List(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_ListAuthorized tests the "list_authorized" method on the "wallet" canister. +func Test_ListAuthorized(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "list_authorized", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new([]principal.Principal)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.ListAuthorized(); err != nil { + t.Fatal(err) + } + +} + +// Test_ListPermitted tests the "list_permitted" method on the "wallet" canister. +func Test_ListPermitted(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "list_permitted", + Arguments: []any{new(wallet.ListPermitted)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new([]principal.Principal)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.ListPermitted + if _, err := a.ListPermitted(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_ProposeCommitBatch tests the "propose_commit_batch" method on the "wallet" canister. +func Test_ProposeCommitBatch(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "propose_commit_batch", + Arguments: []any{new(wallet.CommitBatchArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.CommitBatchArguments + if err := a.ProposeCommitBatch(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_RevokePermission tests the "revoke_permission" method on the "wallet" canister. +func Test_RevokePermission(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "revoke_permission", + Arguments: []any{new(wallet.RevokePermission)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.RevokePermission + if err := a.RevokePermission(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_SetAssetContent tests the "set_asset_content" method on the "wallet" canister. +func Test_SetAssetContent(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "set_asset_content", + Arguments: []any{new(wallet.SetAssetContentArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.SetAssetContentArguments + if err := a.SetAssetContent(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_SetAssetProperties tests the "set_asset_properties" method on the "wallet" canister. +func Test_SetAssetProperties(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "set_asset_properties", + Arguments: []any{new(wallet.SetAssetPropertiesArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.SetAssetPropertiesArguments + if err := a.SetAssetProperties(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_Store tests the "store" method on the "wallet" canister. +func Test_Store(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "store", + Arguments: []any{new(struct { + Key wallet.Key `ic:"key"` + ContentType string `ic:"content_type"` + ContentEncoding string `ic:"content_encoding"` + Content []byte `ic:"content"` + Sha256 *[]byte `ic:"sha256,omitempty"` + })}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 struct { + Key wallet.Key `ic:"key"` + ContentType string `ic:"content_type"` + ContentEncoding string `ic:"content_encoding"` + Content []byte `ic:"content"` + Sha256 *[]byte `ic:"sha256,omitempty"` + } + if err := a.Store(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_TakeOwnership tests the "take_ownership" method on the "wallet" canister. +func Test_TakeOwnership(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "take_ownership", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if err := a.TakeOwnership(); err != nil { + t.Fatal(err) + } + +} + +// Test_UnsetAssetContent tests the "unset_asset_content" method on the "wallet" canister. +func Test_UnsetAssetContent(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "unset_asset_content", + Arguments: []any{new(wallet.UnsetAssetContentArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.UnsetAssetContentArguments + if err := a.UnsetAssetContent(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_ValidateCommitProposedBatch tests the "validate_commit_proposed_batch" method on the "wallet" canister. +func Test_ValidateCommitProposedBatch(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "validate_commit_proposed_batch", + Arguments: []any{new(wallet.CommitProposedBatchArguments)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(wallet.ValidationResult)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.CommitProposedBatchArguments + if _, err := a.ValidateCommitProposedBatch(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_ValidateGrantPermission tests the "validate_grant_permission" method on the "wallet" canister. +func Test_ValidateGrantPermission(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "validate_grant_permission", + Arguments: []any{new(wallet.GrantPermission)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(wallet.ValidationResult)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.GrantPermission + if _, err := a.ValidateGrantPermission(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_ValidateRevokePermission tests the "validate_revoke_permission" method on the "wallet" canister. +func Test_ValidateRevokePermission(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "validate_revoke_permission", + Arguments: []any{new(wallet.RevokePermission)}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(wallet.ValidationResult)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + var a0 wallet.RevokePermission + if _, err := a.ValidateRevokePermission(a0); err != nil { + t.Fatal(err) + } + +} + +// Test_ValidateTakeOwnership tests the "validate_take_ownership" method on the "wallet" canister. +func Test_ValidateTakeOwnership(t *testing.T) { + a, err := newAgent([]mock.Method{ + { + Name: "validate_take_ownership", + Arguments: []any{}, + Handler: func(request mock.Request) ([]any, error) { + return []any{*new(wallet.ValidationResult)}, nil + }, + }, + }) + if err != nil { + t.Fatal(err) + } + + if _, err := a.ValidateTakeOwnership(); err != nil { + t.Fatal(err) + } + +} + +// newAgent creates a new agent with the given (mock) methods. +// Runs a mock replica in the background. +func newAgent(methods []mock.Method) (*wallet.Agent, error) { + replica := mock.NewReplica() + canisterId := principal.Principal{Raw: []byte("wallet")} + replica.AddCanister(canisterId, methods) + s := httptest.NewServer(replica) + u, _ := url.Parse(s.URL) + a, err := wallet.NewAgent(canisterId, agent.Config{ + ClientConfig: &agent.ClientConfig{Host: u}, + FetchRootKey: true, + }) + if err != nil { + return nil, err + } + return a, nil +} diff --git a/mock/replica.go b/mock/replica.go index 84651c8..93a3586 100644 --- a/mock/replica.go +++ b/mock/replica.go @@ -10,7 +10,6 @@ import ( "github.com/aviate-labs/agent-go/principal" "github.com/fxamacker/cbor/v2" "io" - "log" "net/http" "strings" ) @@ -108,7 +107,6 @@ func (r *Replica) handleCanister(writer http.ResponseWriter, canisterId, typ str } requestId := agent.NewRequestID(req) requestIdHex := hex.EncodeToString(requestId[:]) - log.Println("received call request", requestIdHex) r.Requests[requestIdHex] = req writer.WriteHeader(http.StatusAccepted) case "query": @@ -117,9 +115,6 @@ func (r *Replica) handleCanister(writer http.ResponseWriter, canisterId, typ str _, _ = writer.Write([]byte("expected query request")) return } - requestId := agent.NewRequestID(req) - requestIdHex := hex.EncodeToString(requestId[:]) - log.Println("received query request", requestIdHex) method, ok := canister.Methods[req.MethodName] if !ok { @@ -165,7 +160,6 @@ func (r *Replica) handleCanister(writer http.ResponseWriter, canisterId, typ str } requestId := req.Paths[0][1] requestIdHex := hex.EncodeToString(requestId) - log.Println("received read_state request", requestIdHex) req, ok := r.Requests[requestIdHex] if !ok { writer.WriteHeader(http.StatusNotFound) @@ -240,7 +234,6 @@ func (r *Replica) handleCanister(writer http.ResponseWriter, canisterId, typ str } func (r *Replica) handleStatus(writer http.ResponseWriter) { - log.Println("getting status") publicKey := r.rootKey.GetPublicKey().Serialize() status := agent.Status{ Version: "golang-mock",