diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..7e6f2579 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,11 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/go +{ + "name": "Go", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/go:0-1-bullseye", + "features": { + "ghcr.io/devcontainers/features/java:1": { + } + } +} diff --git a/.github/workflows/integration.yaml b/.github/workflows/integration.yaml index 2b59e8ab..6bfe523d 100644 --- a/.github/workflows/integration.yaml +++ b/.github/workflows/integration.yaml @@ -6,8 +6,8 @@ jobs: name: integration_test strategy: matrix: - zk-version: ["3.5.8", "3.6.3"] - go-version: ["1.21"] + zk-version: [3.5.8, 3.6.3] + go-version: ['oldstable', 'stable'] runs-on: ubuntu-latest steps: - name: Go ${{ matrix.go-version }} setup diff --git a/.github/workflows/unittest.yaml b/.github/workflows/unittest.yaml new file mode 100644 index 00000000..42d009cd --- /dev/null +++ b/.github/workflows/unittest.yaml @@ -0,0 +1,21 @@ +name: unittest +on: [push, pull_request] + +jobs: + unittest: + name: unittest + strategy: + matrix: + go-version: ['oldstable', 'stable'] + runs-on: ubuntu-latest + steps: + - name: Go ${{ matrix.go-version }} setup + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go-version }} + + - name: Checkout code + uses: actions/checkout@v3 + + - name: Run unittest ${{ matrix.go }} + run: make unittest diff --git a/Makefile b/Makefile index 7b90c217..de1eaf77 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,10 @@ lint: build: go build ./... +.PHONY: unittest +unittest: + go test -timeout 500s -v -race -covermode atomic -skip=Integration ./... + .PHONY: test test: build zookeeper go test -timeout 500s -v -race -covermode atomic -coverprofile=profile.cov $(PACKAGES) diff --git a/cluster_test.go b/cluster_test.go index 20ec1898..ca83d3e6 100644 --- a/cluster_test.go +++ b/cluster_test.go @@ -17,7 +17,7 @@ func (lw logWriter) Write(b []byte) (int, error) { return len(b), nil } -func TestBasicCluster(t *testing.T) { +func TestIntegration_BasicCluster(t *testing.T) { ts, err := StartTestCluster(t, 3, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -52,7 +52,7 @@ func TestBasicCluster(t *testing.T) { } // If the current leader dies, then the session is reestablished with the new one. -func TestClientClusterFailover(t *testing.T) { +func TestIntegration_ClientClusterFailover(t *testing.T) { tc, err := StartTestCluster(t, 3, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -94,7 +94,7 @@ func TestClientClusterFailover(t *testing.T) { // If a ZooKeeper cluster looses quorum then a session is reconnected as soon // as the quorum is restored. -func TestNoQuorum(t *testing.T) { +func TestIntegration_NoQuorum(t *testing.T) { tc, err := StartTestCluster(t, 3, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -190,7 +190,7 @@ func TestNoQuorum(t *testing.T) { } } -func TestWaitForClose(t *testing.T) { +func TestIntegration_WaitForClose(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -226,7 +226,7 @@ CONNECTED: } } -func TestBadSession(t *testing.T) { +func TestIntegration_BadSession(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) diff --git a/conn_test.go b/conn_test.go index 6b96b9d6..a6df4e81 100644 --- a/conn_test.go +++ b/conn_test.go @@ -10,7 +10,7 @@ import ( "time" ) -func TestRecurringReAuthHang(t *testing.T) { +func TestIntegration_RecurringReAuthHang(t *testing.T) { zkC, err := StartTestCluster(t, 3, ioutil.Discard, ioutil.Discard) if err != nil { panic(err) diff --git a/dnshostprovider_test.go b/dnshostprovider_test.go index 61a113a5..f010eb4d 100644 --- a/dnshostprovider_test.go +++ b/dnshostprovider_test.go @@ -16,7 +16,7 @@ func localhostLookupHost(host string) ([]string, error) { // TestDNSHostProviderCreate is just like TestCreate, but with an // overridden HostProvider that ignores the provided hostname. -func TestDNSHostProviderCreate(t *testing.T) { +func TestIntegration_DNSHostProviderCreate(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -96,7 +96,7 @@ var _ HostProvider = &localHostPortsFacade{} // restarts. It wraps the DNSHostProvider in a lightweight facade that // remaps addresses to localhost:$PORT combinations corresponding to // the test ZooKeeper instances. -func TestDNSHostProviderReconnect(t *testing.T) { +func TestIntegration_DNSHostProviderReconnect(t *testing.T) { ts, err := StartTestCluster(t, 3, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) diff --git a/lock_test.go b/lock_test.go index ee8197ba..0495d845 100644 --- a/lock_test.go +++ b/lock_test.go @@ -5,7 +5,7 @@ import ( "time" ) -func TestLock(t *testing.T) { +func TestIntegration_Lock(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -63,7 +63,7 @@ func TestLock(t *testing.T) { // This tests creating a lock with a path that's more than 1 node deep (e.g. "/test-multi-level/lock"), // when a part of that path already exists (i.e. "/test-multi-level" node already exists). -func TestMultiLevelLock(t *testing.T) { +func TestIntegration_MultiLevelLock(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) diff --git a/server_help_test.go b/server_help_test.go index 348ffcb6..80990b4e 100644 --- a/server_help_test.go +++ b/server_help_test.go @@ -72,6 +72,8 @@ func WithTestCluster(t *testing.T, testTimeout time.Duration, f func(ts *TestClu // testing. This should be used on CI systems and local only when needed whereas unit tests should remain // fast and not rely on external dependencies. func StartTestCluster(t *testing.T, size int, stdout, stderr io.Writer) (*TestCluster, error) { + t.Helper() + if testing.Short() { t.Skip("ZK cluster tests skipped in short case.") } diff --git a/server_java_test.go b/server_java_test.go index dcada4a9..a1956363 100644 --- a/server_java_test.go +++ b/server_java_test.go @@ -67,7 +67,7 @@ func (srv *server) Start() error { srv.cmd = exec.CommandContext(ctx, srv.cmdString, srv.cmdArgs...) srv.cmd.Stdout = srv.stdout srv.cmd.Stderr = srv.stderr - srv.cmd.Env = srv.cmdEnv + srv.cmd.Env = append(os.Environ(), srv.cmdEnv...) return srv.cmd.Start() } diff --git a/zk_test.go b/zk_test.go index 2073a715..8756cdf2 100644 --- a/zk_test.go +++ b/zk_test.go @@ -19,7 +19,7 @@ import ( "time" ) -func TestStateChanges(t *testing.T) { +func TestIntegration_StateChanges(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -65,7 +65,7 @@ func TestStateChanges(t *testing.T) { verifyEventOrder(eventChan, []State{StateDisconnected}, "event channel") } -func TestCreate(t *testing.T) { +func TestIntegration_Create(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -96,7 +96,7 @@ func TestCreate(t *testing.T) { } } -func TestCreateTTL(t *testing.T) { +func TestIntegration_CreateTTL(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -148,7 +148,7 @@ func TestCreateTTL(t *testing.T) { } } -func TestCreateContainer(t *testing.T) { +func TestIntegration_CreateContainer(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -185,7 +185,7 @@ func TestCreateContainer(t *testing.T) { } } -func TestIncrementalReconfig(t *testing.T) { +func TestIntegration_IncrementalReconfig(t *testing.T) { RequireMinimumZkVersion(t, "3.5") ts, err := StartTestCluster(t, 3, nil, logWriter{t: t, p: "[ZKERR] "}) @@ -275,7 +275,7 @@ func TestIncrementalReconfig(t *testing.T) { } } -func TestReconfig(t *testing.T) { +func TestIntegration_Reconfig(t *testing.T) { RequireMinimumZkVersion(t, "3.5") // This test enures we can do an non-incremental reconfig @@ -320,7 +320,7 @@ func TestReconfig(t *testing.T) { requireNoErrorf(t, err, "failed to reconfig cluster") } -func TestOpsAfterCloseDontDeadlock(t *testing.T) { +func TestIntegration_OpsAfterCloseDontDeadlock(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -351,7 +351,7 @@ func TestOpsAfterCloseDontDeadlock(t *testing.T) { } } -func TestMulti(t *testing.T) { +func TestIntegration_Multi(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -388,8 +388,9 @@ func TestMulti(t *testing.T) { } } -func TestMultiRead(t *testing.T) { +func TestIntegration_MultiRead(t *testing.T) { RequireMinimumZkVersion(t, "3.6") + WithTestCluster(t, 10*time.Second, func(ts *TestCluster, zk *Conn) { nodeChildren := map[string][]string{} nodeData := map[string][]byte{} @@ -478,8 +479,9 @@ func TestMultiRead(t *testing.T) { }) } -func TestGetDataAndChildren(t *testing.T) { +func TestIntegration_GetDataAndChildren(t *testing.T) { RequireMinimumZkVersion(t, "3.6") + WithTestCluster(t, 10*time.Second, func(ts *TestCluster, zk *Conn) { const path = "/test" @@ -516,7 +518,7 @@ func TestGetDataAndChildren(t *testing.T) { }) } -func TestIfAuthdataSurvivesReconnect(t *testing.T) { +func TestIntegration_IfAuthdataSurvivesReconnect(t *testing.T) { // This test case ensures authentication data is being resubmited after // reconnect. testNode := "/auth-testnode" @@ -568,8 +570,9 @@ func TestIfAuthdataSurvivesReconnect(t *testing.T) { } } -func TestPersistentWatchOnReconnect(t *testing.T) { +func TestIntegration_PersistentWatchOnReconnect(t *testing.T) { RequireMinimumZkVersion(t, "3.6") + WithTestCluster(t, 10*time.Second, func(ts *TestCluster, zk *Conn) { zk.reconnectLatch = make(chan struct{}) @@ -647,8 +650,9 @@ func waitForEvent(t *testing.T, timeout time.Duration, ch EventQueue, expectedTy return e } -func TestPersistentWatchOnClose(t *testing.T) { +func TestIntegration_PersistentWatchOnClose(t *testing.T) { RequireMinimumZkVersion(t, "3.6") + WithTestCluster(t, 10*time.Second, func(_ *TestCluster, zk *Conn) { ch, err := zk.AddPersistentWatch("/", AddWatchModePersistent) requireNoErrorf(t, err, "could not add persistent watch") @@ -659,8 +663,9 @@ func TestPersistentWatchOnClose(t *testing.T) { }) } -func TestPersistentWatchGetsPinged(t *testing.T) { +func TestIntegration_PersistentWatchGetsPinged(t *testing.T) { RequireMinimumZkVersion(t, "3.6") + WithTestCluster(t, 60*time.Second, func(_ *TestCluster, zk *Conn) { ch, err := zk.AddPersistentWatch("/", AddWatchModePersistent) if err != nil { @@ -672,7 +677,7 @@ func TestPersistentWatchGetsPinged(t *testing.T) { }) } -func TestMultiFailures(t *testing.T) { +func TestIntegration_MultiFailures(t *testing.T) { // This test case ensures that we return the errors associated with each // opeThis in the event a call to Multi() fails. const firstPath = "/gozk-test-first" @@ -720,7 +725,7 @@ func TestMultiFailures(t *testing.T) { } } -func TestGetSetACL(t *testing.T) { +func TestIntegration_GetSetACL(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -774,7 +779,7 @@ func TestGetSetACL(t *testing.T) { } } -func TestAuth(t *testing.T) { +func TestIntegration_Auth(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -825,7 +830,7 @@ func TestAuth(t *testing.T) { } // Tests that we correctly handle a response larger than the default buffer size -func TestChildren(t *testing.T) { +func TestIntegration_Children(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -884,7 +889,7 @@ func TestChildren(t *testing.T) { } } -func TestChildWatch(t *testing.T) { +func TestIntegration_ChildWatch(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -955,7 +960,7 @@ func TestChildWatch(t *testing.T) { } } -func TestSetWatchers(t *testing.T) { +func TestIntegration_SetWatchers(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -1104,7 +1109,7 @@ func TestSetWatchers(t *testing.T) { } } -func TestExpiringWatch(t *testing.T) { +func TestIntegration_ExpiringWatch(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -1180,7 +1185,7 @@ func TestIdempotentClose(t *testing.T) { zk.Close() } -func TestSlowServer(t *testing.T) { +func TestIntegration_SlowServer(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err) @@ -1239,7 +1244,7 @@ func TestSlowServer(t *testing.T) { } } -func TestMaxBufferSize(t *testing.T) { +func TestIntegration_MaxBufferSize(t *testing.T) { ts, err := StartTestCluster(t, 1, nil, logWriter{t: t, p: "[ZKERR] "}) if err != nil { t.Fatal(err)