Skip to content

Commit

Permalink
Add endpoints + check list.
Browse files Browse the repository at this point in the history
  • Loading branch information
q-uint committed May 4, 2024
1 parent 5559491 commit 96ca38a
Show file tree
Hide file tree
Showing 7 changed files with 198 additions and 44 deletions.
76 changes: 37 additions & 39 deletions pocketic/README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,45 @@
# PocketIC Golang: A Canister Testing Library

The client is currently implemented for an unreleased version of the PocketIC server.
The client requires at least version 4 of the PocketIC server.
The client is not yet stable and is subject to change.

You can download the server [here](https://download.dfinity.systems/ic/136a026d67139ecddbc48db3050e488a3c29bb74/binaries/x86_64-linux/pocket-ic.gz).

```go
package actor_test

import (
"os"
"testing"

"github.com/aviate-labs/agent-go/pocketic"
)

func TestActor(t *testing.T) {
pic, err := pocketic.New(pocketic.DefaultSubnetConfig)
if err != nil {
t.Fatal(err)
}

wasmModule, err := os.ReadFile("actor.wasm")
if err != nil {
t.Fatal(err)
}

cID, err := pic.CreateAndInstallCanister(wasmModule, nil, nil)
if err != nil {
t.Fatal(err)
}

// Call the actor, it has native support for the idl types of the agent-go library.
var greeting string
if err := pic.QueryCall(*cID, "hello", nil, []any{&greeting}); err != nil {
t.Fatal(err)
}
_ = greeting
}

```

## References

- [PocketIC](https://github.com/dfinity/pocketic)
- [PocketIC Server](https://github.com/dfinity/ic/tree/master/rs/pocket_ic_server)

## List of Supported Endpoints

| Supported | Method | Endpoint |
|-----------|--------|---------------------------------------------------|
|| GET | /status |
|| POST | /blobstore |
|| GET | /blobstore/{id} |
|| POST | /verify_signature |
|| GET | /read_graph/{state_label}/{op_id} |
|| GET | /instances/ |
|| POST | /instances/ |
|| DELETE | /instances/{id} |
|| POST | /instances/{id}/read/query |
|| GET | /instances/{id}/read/get_time |
|| POST | /instances/{id}/read/get_cycles |
|| POST | /instances/{id}/read/get_stable_memory |
|| POST | /instances/{id}/read/get_subnet |
|| POST | /instances/{id}/read/pub_key |
|| POST | /instances/{id}/update/submit_ingress_message |
|| POST | /instances/{id}/update/await_ingress_message |
|| POST | /instances/{id}/update/execute_ingress_message |
|| POST | /instances/{id}/update/set_time |
|| POST | /instances/{id}/update/add_cycles |
|| POST | /instances/{id}/update/set_stable_memory |
|| POST | /instances/{id}/update/tick |
|| GET | /instances/{id}/api/v2/status |
|| POST | /instances/{id}/api/v2/canister/{ecid}/call |
|| POST | /instances/{id}/api/v2/canister/{ecid}/query |
|| POST | /instances/{id}/api/v2/canister/{ecid}/read_state |
|| POST | /instances/{id}/auto_progress |
|| POST | /instances/{id}/stop_progress |
|| POST | /http_gateway/ |
|| POST | /http_gateway/{id}/stop |


51 changes: 51 additions & 0 deletions pocketic/blobstore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package pocketic

import (
"encoding/hex"
"fmt"
"io"
"net/http"
)

// GetBlob retrieves a binary blob from the PocketIC server.
func (pic PocketIC) GetBlob(blobID []byte) ([]byte, error) {
var bytes []byte
if err := pic.do(
http.MethodGet,
fmt.Sprintf("%s/blobstore/%s", pic.server.URL(), hex.EncodeToString(blobID)),
http.StatusOK,
nil,
&bytes,
); err != nil {
return nil, err
}
return bytes, nil
}

// UploadBlob uploads and stores a binary blob to the PocketIC server.
func (pic PocketIC) UploadBlob(bytes []byte) ([]byte, error) {
method := http.MethodPost
url := fmt.Sprintf("%s/blobstore", pic.server.URL())
pic.logger.Printf("[POCKETIC] %s %s %+v", method, url, bytes)
req, err := newRequest(method, url, bytes)
if err != nil {
return nil, err
}
req.Header.Set("content-type", "application/octet-stream")
resp, err := pic.client.Do(req)
if err != nil {
return nil, err
}
hexBlobID, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
blobID, err := hex.DecodeString(string(hexBlobID))
if err != nil {
return nil, err
}
return blobID, nil
}
41 changes: 41 additions & 0 deletions pocketic/endpoints_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package pocketic_test

import (
"github.com/aviate-labs/agent-go/pocketic"
"testing"
)

func TestEndpoints(t *testing.T) {
pic, err := pocketic.New(
pocketic.WithLogger(new(testLogger)),
pocketic.WithNNSSubnet(),
pocketic.WithApplicationSubnet(),
)
if err != nil {
t.Fatal(err)
}
defer func() {
if err := pic.Close(); err != nil {
t.Fatal(err)
}
}()

t.Run("status", func(t *testing.T) {
if err := pic.Status(); err != nil {
t.Fatal(err)
}
})
t.Run("blobstore", func(t *testing.T) {
id, err := pic.UploadBlob([]byte{0, 1, 2, 3})
if err != nil {
t.Fatal(err)
}
bytes, err := pic.GetBlob(id)
if err != nil {
t.Fatal(err)
}
if len(bytes) != 4 {
t.Fatalf("unexpected blob size: %d", len(bytes))
}
})
}
36 changes: 36 additions & 0 deletions pocketic/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ func (pic PocketIC) AutoProgress() error {
)
}

// MakeDeterministic makes the IC instance deterministic by stopping automatic progress (time updates and round
// executions) on the IC instance and stops the HTTP gateway for this IC instance.
func (pic PocketIC) MakeDeterministic() error {
if err := pic.stopHttpGateway(); err != nil {
return err
}
return pic.StopProgress()
}

// MakeLive creates an HTTP gateway for this IC instance listening on an optionally specified port and configures the IC
// instance to make progress automatically, i.e., periodically update the time of the IC to the real time and execute
// rounds on the subnets. Returns the URL at which `/api/v2` requests for this instance can be made.
Expand Down Expand Up @@ -129,3 +138,30 @@ func (pic PocketIC) SetTime(time time.Time) error {
nil,
)
}

// StopProgress stops automatic progress (see `auto_progress`) on the IC.
func (pic PocketIC) StopProgress() error {
return pic.do(
http.MethodPost,
fmt.Sprintf("%s/stop_progress", pic.instanceURL()),
http.StatusOK,
nil,
nil,
)
}

func (pic *PocketIC) stopHttpGateway() error {
if pic.httpGateway != nil {
if err := pic.do(
http.MethodPost,
fmt.Sprintf("%s/http_gateway/%d/stop", pic.server.URL(), pic.httpGateway.InstanceID),
http.StatusOK,
nil,
nil,
); err != nil {
return err
}
pic.httpGateway = nil
}
return nil
}
1 change: 1 addition & 0 deletions pocketic/management.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/aviate-labs/agent-go/principal"
)

// AddCycles add cycles to a canister. Returns the new balance.
func (pic PocketIC) AddCycles(canisterID principal.Principal, amount int) (int, error) {
var resp struct {
Cycles int `json:"cycles"`
Expand Down
10 changes: 10 additions & 0 deletions pocketic/pocketic.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,16 @@ func (pic *PocketIC) Close() error {
return pic.server.Close()
}

func (pic PocketIC) Status() error {
return pic.do(
http.MethodGet,
fmt.Sprintf("%s/status", pic.server.URL()),
http.StatusOK,
nil,
nil,
)
}

// Topology returns the topology of the PocketIC instance.
func (pic PocketIC) Topology() map[string]Topology {
return pic.topology
Expand Down
27 changes: 22 additions & 5 deletions pocketic/pocketic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func CreateCanister(t *testing.T) *pocketic.PocketIC {
return pic
}

func MakeLive(t *testing.T) *pocketic.PocketIC {
func HttpGateway(t *testing.T) *pocketic.PocketIC {
pic, err := pocketic.New(
pocketic.WithLogger(new(testLogger)),
pocketic.WithNNSSubnet(),
Expand All @@ -52,11 +52,12 @@ func MakeLive(t *testing.T) *pocketic.PocketIC {
t.Fatal(err)
}

mgmtAgent, err := ic0.NewAgent(ic.MANAGEMENT_CANISTER_PRINCIPAL, agent.Config{
agentConfig := agent.Config{
ClientConfig: &agent.ClientConfig{Host: host},
FetchRootKey: true,
Logger: new(testLogger),
})
}
mgmtAgent, err := ic0.NewAgent(ic.MANAGEMENT_CANISTER_PRINCIPAL, agentConfig)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -93,6 +94,22 @@ func MakeLive(t *testing.T) *pocketic.PocketIC {
t.Fatal(err)
}

helloAgent, err := NewAgent(result.CanisterId, agentConfig)
if err != nil {
t.Fatal(err)
}
resp, err := helloAgent.HelloUpdate("world")
if err != nil {
t.Fatal(err)
}
if *resp != "Hello, world!" {
t.Fatalf("unexpected response: %s", *resp)
}

if err := pic.MakeDeterministic(); err != nil {
t.Fatal(err)
}

return pic
}

Expand All @@ -101,8 +118,8 @@ func TestPocketIC(t *testing.T) {
t.Run("CreateCanister", func(t *testing.T) {
instances = append(instances, CreateCanister(t))
})
t.Run("MakeLive", func(t *testing.T) {
instances = append(instances, MakeLive(t))
t.Run("HttpGateway", func(t *testing.T) {
instances = append(instances, HttpGateway(t))
})
for _, i := range instances {
if err := i.Close(); err != nil {
Expand Down

0 comments on commit 96ca38a

Please sign in to comment.