Skip to content

Commit 66a0626

Browse files
ex172000claude
andcommitted
feat(bdd): add shared stream operations scenario for Go SDK (#1986)
Add a shared Gherkin feature file for stream CRUD operations and corresponding godog step definitions, beginning the migration of Go BDD tests from embedded Ginkgo scenarios to shared .feature files. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 30fae4c commit 66a0626

3 files changed

Lines changed: 204 additions & 0 deletions

File tree

bdd/docker-compose.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ services:
225225
- GO_TEST_EXTRA_FLAGS=${GO_TEST_EXTRA_FLAGS:-}
226226
volumes:
227227
- ./scenarios/basic_messaging.feature:/app/features/basic_messaging.feature
228+
- ./scenarios/stream_operations.feature:/app/features/stream_operations.feature
228229
command: [ "sh", "-c", "go test -v ${GO_TEST_EXTRA_FLAGS} ./..." ]
229230
networks:
230231
- iggy-bdd-network
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package tests
19+
20+
import (
21+
"context"
22+
"fmt"
23+
"math/rand"
24+
"os"
25+
"testing"
26+
27+
iggcon "github.com/apache/iggy/foreign/go/contracts"
28+
"github.com/apache/iggy/foreign/go/iggycli"
29+
"github.com/apache/iggy/foreign/go/tcp"
30+
"github.com/cucumber/godog"
31+
)
32+
33+
type streamOpsCtxKey struct{}
34+
35+
type streamOpsCtx struct {
36+
serverAddr *string
37+
client iggycli.Client
38+
streamID *uint32
39+
streamName *string
40+
}
41+
42+
func getStreamOpsCtx(ctx context.Context) *streamOpsCtx {
43+
return ctx.Value(streamOpsCtxKey{}).(*streamOpsCtx)
44+
}
45+
46+
func streamGivenRunningServer(ctx context.Context) error {
47+
c := getStreamOpsCtx(ctx)
48+
addr := os.Getenv("IGGY_TCP_ADDRESS")
49+
if addr == "" {
50+
addr = "127.0.0.1:8090"
51+
}
52+
c.serverAddr = &addr
53+
return nil
54+
}
55+
56+
func streamGivenAuthenticationAsRoot(ctx context.Context) error {
57+
c := getStreamOpsCtx(ctx)
58+
serverAddr := *c.serverAddr
59+
60+
client, err := iggycli.NewIggyClient(
61+
iggycli.WithTcp(
62+
tcp.WithServerAddress(serverAddr),
63+
),
64+
)
65+
if err != nil {
66+
return fmt.Errorf("error creating client: %w", err)
67+
}
68+
69+
if err = client.Ping(); err != nil {
70+
return fmt.Errorf("error pinging client: %w", err)
71+
}
72+
73+
if _, err = client.LoginUser("iggy", "iggy"); err != nil {
74+
return fmt.Errorf("error logging in: %v", err)
75+
}
76+
77+
c.client = client
78+
return nil
79+
}
80+
81+
func whenCreateStreamWithUniqueName(ctx context.Context) error {
82+
c := getStreamOpsCtx(ctx)
83+
84+
const charset = "abcdefghijklmnopqrstuvwxyz0123456789"
85+
b := make([]byte, 32)
86+
for i := range b {
87+
b[i] = charset[rand.Intn(len(charset))]
88+
}
89+
name := string(b)
90+
91+
stream, err := c.client.CreateStream(name)
92+
if err != nil {
93+
return fmt.Errorf("failed to create stream: %w", err)
94+
}
95+
96+
c.streamID = &stream.Id
97+
c.streamName = &stream.Name
98+
return nil
99+
}
100+
101+
func thenStreamCreatedSuccessfullyOps(ctx context.Context) error {
102+
c := getStreamOpsCtx(ctx)
103+
if c.streamID == nil {
104+
return fmt.Errorf("stream should have been created")
105+
}
106+
return nil
107+
}
108+
109+
func thenStreamRetrievableByID(ctx context.Context) error {
110+
c := getStreamOpsCtx(ctx)
111+
streamIdentifier, _ := iggcon.NewIdentifier(*c.streamID)
112+
stream, err := c.client.GetStream(streamIdentifier)
113+
if err != nil {
114+
return fmt.Errorf("failed to get stream by ID: %w", err)
115+
}
116+
if stream == nil {
117+
return fmt.Errorf("stream should not be nil")
118+
}
119+
return nil
120+
}
121+
122+
func thenStreamNameMatchesCreated(ctx context.Context) error {
123+
c := getStreamOpsCtx(ctx)
124+
streamIdentifier, _ := iggcon.NewIdentifier(*c.streamID)
125+
stream, err := c.client.GetStream(streamIdentifier)
126+
if err != nil {
127+
return fmt.Errorf("failed to get stream: %w", err)
128+
}
129+
if stream.Name != *c.streamName {
130+
return fmt.Errorf("expected stream name %s, got %s", *c.streamName, stream.Name)
131+
}
132+
return nil
133+
}
134+
135+
func initStreamScenarios(sc *godog.ScenarioContext) {
136+
sc.Before(func(ctx context.Context, sc *godog.Scenario) (context.Context, error) {
137+
return context.WithValue(context.Background(), streamOpsCtxKey{}, &streamOpsCtx{}), nil
138+
})
139+
140+
sc.After(func(ctx context.Context, sc *godog.Scenario, err error) (context.Context, error) {
141+
c := getStreamOpsCtx(ctx)
142+
if c.client != nil && c.streamID != nil {
143+
streamIdentifier, _ := iggcon.NewIdentifier(*c.streamID)
144+
_ = c.client.DeleteStream(streamIdentifier)
145+
}
146+
return ctx, nil
147+
})
148+
149+
// Background steps
150+
sc.Step(`I have a running Iggy server`, streamGivenRunningServer)
151+
sc.Step(`I am authenticated as the root user`, streamGivenAuthenticationAsRoot)
152+
153+
// Stream operation steps
154+
sc.Step(`I create a stream with a unique name`, whenCreateStreamWithUniqueName)
155+
sc.Step(`the stream should be created successfully`, thenStreamCreatedSuccessfullyOps)
156+
sc.Step(`the stream should be retrievable by its ID`, thenStreamRetrievableByID)
157+
sc.Step(`the stream name should match the created name`, thenStreamNameMatchesCreated)
158+
}
159+
160+
func TestStreamFeatures(t *testing.T) {
161+
suite := godog.TestSuite{
162+
ScenarioInitializer: initStreamScenarios,
163+
Options: &godog.Options{
164+
Format: "pretty",
165+
Paths: []string{"../../scenarios/stream_operations.feature"},
166+
TestingT: t,
167+
},
168+
}
169+
if suite.Run() != 0 {
170+
t.Fatal("failing stream feature tests")
171+
}
172+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
Feature: Stream Operations
19+
As a developer using Apache Iggy
20+
I want to manage streams
21+
So that I can organize my messaging data
22+
23+
Background:
24+
Given I have a running Iggy server
25+
And I am authenticated as the root user
26+
27+
Scenario: Create stream with valid name
28+
When I create a stream with a unique name
29+
Then the stream should be created successfully
30+
And the stream should be retrievable by its ID
31+
And the stream name should match the created name

0 commit comments

Comments
 (0)