diff --git a/.github/filters-old.yaml b/.github/filters-old.yaml deleted file mode 100644 index 73cccea88..000000000 --- a/.github/filters-old.yaml +++ /dev/null @@ -1,19 +0,0 @@ -code: - - added|modified: 'go.mod' - - added|modified: '.github/filters.yaml' - - added|modified: '.github/workflows/ci.yaml' - - added|modified: 'build/package/docker/kwild.dockerfile' - - added|modified: 'cmd/**' - - added|modified: 'common/**' - - added|modified: 'core/**/*.go' - - added|modified: 'core/!(*.md)' - - added|modified: 'core/client/!(*.md)' - - added|modified: 'core/client/!(example/**)/**' - - added|modified: 'core/gatewayclient/!(*.md)' - - added|modified: 'core/gatewayclient/!(example/**)/**' - - added|modified: 'internal/**' - - added|modified: 'parse/**' - - added|modified: 'scripts/**/*' - - added|modified: 'test/**/*' - - 'test/!(stress/**)/**' - - 'parse/!(grammar)/**' \ No newline at end of file diff --git a/.github/workflows/ci-main.yaml b/.github/workflows/ci-main.yaml index 8f524cb85..b98b83338 100644 --- a/.github/workflows/ci-main.yaml +++ b/.github/workflows/ci-main.yaml @@ -121,25 +121,22 @@ jobs: task build:cli task build:kwild - # - name: compile the core/client/example app - # run: go build -o /dev/null - # working-directory: core/client/example - - # - name: compile the core/gatewayclient/example app - # run: go build -o /dev/null - # working-directory: core/gatewayclient/example +# - name: compile the core/client/example app +# run: go build -o /dev/null +# working-directory: core/client/example +# +# - name: compile the core/gatewayclient/example app +# run: go build -o /dev/null +# working-directory: core/gatewayclient/example - name: Generate go vendor - if: false run: | task vendor - name: Set up Docker Buildx - if: false uses: docker/setup-buildx-action@v3 - name: Cache Docker layers for kwild # both restore and save - if: false uses: actions/cache@v4 with: path: /tmp/.buildx-cache-kwild @@ -148,7 +145,6 @@ jobs: ${{ runner.os }}-buildx-kwild - name: Build kwild image - if: false id: docker_build_kwild uses: docker/build-push-action@v6 with: @@ -166,13 +162,11 @@ jobs: cache-from: type=local,src=/tmp/.buildx-cache-kwild cache-to: type=local,dest=/tmp/.buildx-cache-kwild-new - # - name: Run acceptance test - # run: | - # testUserID=$(id -u) - # testGroupID=$(id -g) - # cp test/acceptance/docker-compose.override.yml.example test/acceptance/docker-compose.override.yml - # sed -i "s/\${UID}:\${GID}/${testUserID}:${testGroupID}/g" test/acceptance/docker-compose.override.yml - # KACT_LOG_LEVEL=warn task test:act:nb + - name: Run acceptance test + run: | + testUserID=$(id -u) + testGroupID=$(id -g) + KACT_LOG_LEVEL=warn task test:act:nb -- -ugid "$testUserID:$testGroupID" # - name: Run integration test # run: | @@ -185,15 +179,14 @@ jobs: # KIT_LOG_LEVEL=warn task test:it:nb - name: Move cache - if: false run: | rm -rf /tmp/.buildx-cache-kwild mv /tmp/.buildx-cache-kwild-new /tmp/.buildx-cache-kwild - # - name: Prune Docker - # if: ${{ always() }} - # run: docker rm $(docker ps -a -q) -f ; docker network prune -f ; docker volume prune -f || true + - name: Prune Docker + if: ${{ always() }} + run: docker rm $(docker ps -a -q) -f ; docker network prune -f ; docker volume prune -f || true - # - name: Show error log - # if: ${{ failure() }} - # run: grep -C 20 -s -i -r -e 'kwild version' -e 'error' -e 'warn' /tmp/TestKwilAct*/*.log /tmp/TestKwilInt*/*.log /tmp/TestKwilInt*/*/*.log + - name: Show error log + if: ${{ failure() }} + run: grep -C 20 -s -i -r -e 'kwild version' -e 'error' -e 'warn' /tmp/TestKwilAct*/*.log /tmp/TestKwilInt*/*.log /tmp/TestKwilInt*/*/*.log diff --git a/.github/workflows/kgw-test-reuse.yaml b/.github/workflows/kgw-test-reuse.yaml index 5ac696431..f4bf478bb 100644 --- a/.github/workflows/kgw-test-reuse.yaml +++ b/.github/workflows/kgw-test-reuse.yaml @@ -108,8 +108,8 @@ jobs: # vendor is used to bypass private repo issues; # if kgw on non-release branches, we want to use go workspace, so that kgw # always uses the latest version of kwil-db/core + # when build in workspace, the dockerfile requires kgw and kwild in same folder. run: | - echo "KWK_DIR=$(pwd)/.." >> $GITHUB_ENV kdbDir=$(pwd) echo "KDB_DIR=$(pwd)" >> $GITHUB_ENV echo "current dir: " $kdbDir @@ -129,10 +129,11 @@ jobs: fi cd - cd $kdbDir + echo "current dir: " $pwd - name: Build kgw image id: docker_build_kgw - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: ${{ env.KGW_DIR }}/.. load: true @@ -163,7 +164,7 @@ jobs: - name: Build kwild image id: docker_build_kwild - uses: docker/build-push-action@v5 + uses: docker/build-push-action@v6 with: context: . load: true @@ -172,7 +173,7 @@ jobs: git_commit=${{ github.sha }} version=${{ env.GIT_TAG }} build_time=${{ env.BUILD_TIME }} - file: ./build/package/docker/kwild.dockerfile + file: ./contrib/docker/kwild.dockerfile push: false tags: kwild:latest cache-from: type=local,src=/tmp/.buildx-cache-kwild @@ -182,17 +183,15 @@ jobs: run: | task build:cli - - name: Run kgw integration test + - name: Run kgw acceptance test run: | testUserID=$(id -u) testGroupID=$(id -g) - cp test/integration/docker-compose.override.yml.example test/integration/docker-compose.override.yml - sed -i "s/\${UID}:\${GID}/${testUserID}:${testGroupID}/g" test/integration/docker-compose.override.yml - KIT_LOG_LEVEL=warn go test -count=1 -timeout 0 ./test/integration -run ^TestKGW$ -v + KACT_LOG_LEVEL=warn task test:act:nb -- -kgw -ugid "$testUserID:$testGroupID" - name: Run kwil-js test against kwild & kgw run: | - cd deployments/compose/kwil + cd contrib/docker/compose/kwil echo "run kwild in docker" docker compose up -d # clone kwil-js @@ -214,7 +213,6 @@ jobs: -b "http://kwild:8484" \ --chain-id $chain_id \ --allow-adhoc-query \ - --allow-deploy-db \ --log-level debug echo "run KWIL-JS TEST against kwild(http://localhost:8484), with $chain_id" PRIVATE_KEY=0000000000000000000000000000000000000000000000000000000000000001 CHAIN_ID=$chain_id GATEWAY_ON=false GAS_ON=false KWIL_PROVIDER=http://localhost:8484 npm run integration diff --git a/test/acceptance/act_test.go b/test/acceptance/act_test.go index f3b8403a6..ebb6b711d 100644 --- a/test/acceptance/act_test.go +++ b/test/acceptance/act_test.go @@ -24,10 +24,10 @@ import ( ) // TODO: -// - kgw tests // - log / "notice()" tests var dev = flag.Bool("dev", false, "run for development purpose (no tests)") +var withKGW = flag.Bool("kgw", false, "test with kgw") func TestLocalDevSetup(t *testing.T) { if !*dev { @@ -44,7 +44,7 @@ func TestLocalDevSetup(t *testing.T) { cancel() }() - client := setupSingleNodeClient(t, ctx, setup.Go, false) + client := setupSingleNodeClient(t, ctx, setup.Go, *withKGW) ci, err := client.ChainInfo(ctx) if err != nil { t.Fatal(err) @@ -69,17 +69,34 @@ var ( } return privk }() + + kgwSvc = &setup.CustomService{ + ServiceName: "kgw", + DockerImage: "kgw:latest", + Command: `--log-level debug --log-outputs stdout /app/kgw.log --cors-allow-origins * --backends node0:8484 node1:8484 --domain http://localhost:8090 --statement 'Trust me ok?' --session-secret 'kgwtest' --chain-id kwil-testnet --allow-adhoc-query --devmode`, + ExposedPort: "8090", + InternalPort: "8090", + ServiceProto: "http", + WaitMsg: "KGW Server started", + DependsOn: "node0", + } ) -// setupSingleNodeClient creates a single node network for testing, -// and returns the client -func setupSingleNodeClient(t *testing.T, ctx context.Context, d setup.ClientDriver, usingKGW bool) setup.JSONRPCClient { +// setupSingleNodeClient creates a single node network for testing, and returns the client. +// If useKGW=true, a KGW node will be created, the returned client will talk to KGW instead of the kwild node. +func setupSingleNodeClient(t *testing.T, ctx context.Context, d setup.ClientDriver, useKGW bool) setup.JSONRPCClient { t.Helper() signer := auth.GetUserSigner(UserPrivkey1) ident, err := auth.EthSecp256k1Authenticator{}.Identifier(signer.CompactID()) require.NoError(t, err) + var extraServices []*setup.CustomService + + if useKGW { + extraServices = append(extraServices, kgwSvc) + } + testnet := setup.SetupTests(t, &setup.TestConfig{ ClientDriver: d, Network: &setup.NetworkConfig{ @@ -87,12 +104,21 @@ func setupSingleNodeClient(t *testing.T, ctx context.Context, d setup.ClientDriv Nodes: []*setup.NodeConfig{ setup.DefaultNodeConfig(), }, + ExtraServices: extraServices, }, }) - return testnet.Nodes[0].JSONRPCClient(t, ctx, &setup.ClientOptions{ - UsingKGW: usingKGW, + + opts := &setup.ClientOptions{ PrivateKey: UserPrivkey1, - }) + } + + kgwNode, ok := testnet.SearchExtraNode("kgw") + if ok { + opts.UsingKGW = true + opts.Endpoint = kgwNode.ExposedChainRPC + } + + return testnet.Nodes[0].JSONRPCClient(t, ctx, opts) } func Test_Transfer(t *testing.T) { @@ -118,6 +144,11 @@ func Test_Transfer(t *testing.T) { return val } + var extraServices []*setup.CustomService + if *withKGW { + extraServices = append(extraServices, kgwSvc) + } + testnet := setup.SetupTests(t, &setup.TestConfig{ ClientDriver: driver, Network: &setup.NetworkConfig{ @@ -134,14 +165,23 @@ func Test_Transfer(t *testing.T) { Nodes: []*setup.NodeConfig{ setup.DefaultNodeConfig(), }, - DBOwner: stringAddress(userPrivateKey), + DBOwner: stringAddress(userPrivateKey), + ExtraServices: extraServices, }, }) - // user 1 will send funds to user 2. User 2 will check that they received the funds - user1 := testnet.Nodes[0].JSONRPCClient(t, ctx, &setup.ClientOptions{ + cltOpts := &setup.ClientOptions{ PrivateKey: userPrivateKey, - }) + } + + kgwNode, ok := testnet.SearchExtraNode("kgw") + if ok { + cltOpts.UsingKGW = true + cltOpts.Endpoint = kgwNode.ExposedChainRPC + } + + // user 1 will send funds to user 2. User 2 will check that they received the funds + user1 := testnet.Nodes[0].JSONRPCClient(t, ctx, cltOpts) // user 1 creates an action, which user 2 will call to test they have funds tx, err := user1.ExecuteSQL(ctx, "CREATE ACTION do_something() public {}", nil, opts) @@ -178,7 +218,7 @@ func Test_Engine(t *testing.T) { for _, driver := range setup.AllDrivers { t.Run("engine_"+driver.String(), func(t *testing.T) { ctx := context.Background() - client := setupSingleNodeClient(t, ctx, driver, false) + client := setupSingleNodeClient(t, ctx, driver, *withKGW) // deploy the schema tx, err := client.ExecuteSQL(ctx, usersSchema, nil, opts) @@ -237,7 +277,7 @@ func Test_Roundtrip(t *testing.T) { for _, driver := range setup.AllDrivers { t.Run("roundtrip_"+driver.String(), func(t *testing.T) { ctx := context.Background() - client := setupSingleNodeClient(t, ctx, driver, false) + client := setupSingleNodeClient(t, ctx, driver, *withKGW) // a table that stores all data types tx, err := client.ExecuteSQL(ctx, ` diff --git a/test/setup/compose.go b/test/setup/compose.go index 4b8202ebc..669795f2e 100644 --- a/test/setup/compose.go +++ b/test/setup/compose.go @@ -120,7 +120,9 @@ func generateCompose(dockerNetwork string, testnetDir string, nodeConfs []*NodeC Command: svc.Command, ExposedPort: svc.ExposedPort, InternalPort: svc.InternalPort, + DependsOn: svc.DependsOn, } + if userAndGroupIDs != nil { svcTmpl.UserID = userAndGroupIDs[0] svcTmpl.GroupID = userAndGroupIDs[1] @@ -180,6 +182,7 @@ type serviceTemplate struct { // InternalPort is the port that the service is running on internally. // It must be set if ExposedPort is set InternalPort string + DependsOn string } func (s *serviceTemplate) generate(r *bytes.Buffer) error { @@ -200,6 +203,10 @@ type CustomService struct { // OPTIONAL: InternalPort is the port that the service is running on internally. // It must be set if ExposedPort is set InternalPort string + // OPTIONAL: ServiceProto is the proto that the service use. + ServiceProto string // OPTIONAL: WaitMsg is a log that Docker will wait for before considering the service to be up WaitMsg string + // OPTIONAL: DependsOn specify a service that needs to be healthy + DependsOn string } diff --git a/test/setup/drivers.go b/test/setup/drivers.go index 81c94bebc..64aea9ff7 100644 --- a/test/setup/drivers.go +++ b/test/setup/drivers.go @@ -24,6 +24,8 @@ type ClientOptions struct { PrivateKey crypto.PrivateKey // UsingKGW specifies whether to use the gateway client. UsingKGW bool + // Endpoint specifies the endpoint to use. This is mostly for kgw. + Endpoint string // ChainID is the chain ID to use. ChainID string } diff --git a/test/setup/network.go b/test/setup/network.go index 6ee13d17a..02f931dc3 100644 --- a/test/setup/network.go +++ b/test/setup/network.go @@ -16,6 +16,8 @@ import ( const ( jsonRPCPort = 8484 p2pPort = 6600 + + kgwRPCPort = 8090 ) // EnsureNetworkExist creates a new docker network with a random UUID name. diff --git a/test/setup/node.go b/test/setup/node.go index e22bfc365..d2df97d0c 100644 --- a/test/setup/node.go +++ b/test/setup/node.go @@ -5,9 +5,11 @@ import ( "crypto/rand" "encoding/hex" "errors" + "flag" "fmt" "os" "slices" + "strings" "testing" "time" @@ -24,6 +26,28 @@ import ( "github.com/testcontainers/testcontainers-go/wait" ) +var userGroupID = flag.String("ugid", "", "user id and group id to use for the test containers; format: :") + +func getFlagUserGroupID() (ids *[2]string, err error) { + if *userGroupID == "" { + return ids, nil + } + + if !strings.Contains(*userGroupID, ":") { + return ids, fmt.Errorf("invalid user id and group id format: %s", *userGroupID) + } + + segs := strings.Split(*userGroupID, ":") + if len(segs) != 2 { + return ids, fmt.Errorf("invalid user id and group id format: %s", *userGroupID) + } + + ids = new([2]string) + ids[0] = segs[0] + ids[1] = segs[1] + return ids, nil +} + // TestConfig is the configuration for the test type TestConfig struct { // REQUIRED: ClientDriver is the driver to use for the client @@ -132,10 +156,29 @@ func CustomNodeConfig(f func(*NodeConfig)) *NodeConfig { return cfg } +type ExtraNode struct { + ServiceName string + ExposedChainRPC string + UnexposedChainRPC string +} + type Testnet struct { Nodes []KwilNode testCtx *testingContext EthNode *EthNode + + // include kgw + // TODO: merge EthNode + ExtraNodes []*ExtraNode +} + +func (t *Testnet) SearchExtraNode(name string) (*ExtraNode, bool) { + for _, node := range t.ExtraNodes { + if node.ServiceName == name { + return node, true + } + } + return nil, false } func (t *Testnet) TestDir() string { @@ -193,7 +236,11 @@ func DeployETHNode(t *testing.T, ctx context.Context, dockerName string, privKey } services := []*CustomService{hardHatService} - composePath, _, err := generateCompose(dockerName, tmpDir, nil, services, nil, "", 0) + ugids, err := getFlagUserGroupID() + require.NoError(t, err) + t.Logf("user group id: %v", *ugids) + + composePath, _, err := generateCompose(dockerName, tmpDir, nil, services, ugids, "", 0) require.NoError(t, err) testCtx := &testingContext{ @@ -262,7 +309,11 @@ func SetupTests(t *testing.T, testConfig *TestConfig) *Testnet { dockerNetworkName = testConfig.DockerNetwork } - composePath, nodeInfo, err := generateCompose(dockerNetworkName, tmpDir, testConfig.Network.Nodes, testConfig.Network.ExtraServices, nil, testConfig.ServicesPrefix, testConfig.PortOffset) //TODO: need user id and groups + ugids, err := getFlagUserGroupID() + require.NoError(t, err) + t.Logf("user group id: %v", ugids) + + composePath, nodeInfo, err := generateCompose(dockerNetworkName, tmpDir, testConfig.Network.Nodes, testConfig.Network.ExtraServices, ugids, testConfig.ServicesPrefix, testConfig.PortOffset) //TODO: need user id and groups require.NoError(t, err) require.Equal(t, len(testConfig.Network.Nodes), len(nodeInfo)) // ensure that the number of nodes is the same as the number of node info @@ -400,6 +451,22 @@ func SetupTests(t *testing.T, testConfig *TestConfig) *Testnet { tp.Nodes = append(tp.Nodes, node) } + for _, svc := range testConfig.Network.ExtraServices { + // check if that service is running + ctr, ok := testCtx.containers[svc.ServiceName] + require.True(t, ok, "%s service not found", svc.ServiceName) + + // get the endpoint for the hardhat service + exposedChainRPC, unexposedChainRPC, err := getEndpoints(ctr, ctx, nat.Port(svc.InternalPort), svc.ServiceProto) + require.NoError(t, err, "failed to get endpoints for hardhat service") + + tp.ExtraNodes = append(tp.ExtraNodes, &ExtraNode{ + ServiceName: svc.ServiceName, + ExposedChainRPC: exposedChainRPC, + UnexposedChainRPC: unexposedChainRPC, + }) + } + return tp } @@ -628,6 +695,10 @@ func (k *kwilNode) JSONRPCClient(t *testing.T, ctx context.Context, opts *Client endpoint, _, err := kwildJSONRPCEndpoints(container, ctx) require.NoError(t, err) + if opts != nil && opts.Endpoint != "" { + endpoint = opts.Endpoint + } + client, err := getNewClientFn(k.testCtx.config.ClientDriver)(ctx, endpoint, t.Logf, k.testCtx, opts) require.NoError(t, err) diff --git a/test/setup/other-compose.yml.template b/test/setup/other-compose.yml.template index ee1523a4c..5d7c27010 100644 --- a/test/setup/other-compose.yml.template +++ b/test/setup/other-compose.yml.template @@ -4,6 +4,9 @@ image: {{ .DockerImage }} {{ if .ExposedPort }}ports: - "{{ .ExposedPort }}:{{ .InternalPort }}" {{ end }} + {{ if .DependsOn }}depends_on: + {{ .DependsOn }}: + condition: service_healthy{{ end }} networks: - {{ .Network }} {{ if .Command }}command: |