Skip to content

Commit 2df4b48

Browse files
committed
Stabilize Spanner integration tests
1 parent e06d82b commit 2df4b48

22 files changed

Lines changed: 633 additions & 228 deletions
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

.github/workflows/ci.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,19 @@ jobs:
9090
cache: true
9191
# Spanner tests use a Docker testcontainer emulator, not the embedded
9292
# postgres binary, so no Maven binary cache is needed here.
93-
- run: go test -v -tags spanner_integration -timeout=10m ./...
93+
- name: Spanner integration tests
94+
run: |
95+
mkdir -p test-output/go
96+
set -o pipefail
97+
go test -json -v -tags spanner_integration -timeout=10m ./... | tee test-output/go/spanner-integration.json
98+
- name: Upload Spanner test log on failure
99+
if: failure()
100+
uses: actions/upload-artifact@v7
101+
with:
102+
name: spanner-integration-test-log
103+
path: test-output/go/spanner-integration.json
104+
if-no-files-found: ignore
105+
retention-days: 7
94106

95107
goreleaser-snapshot:
96108
runs-on: depot-ubuntu-24.04-8

internal/api/integration_test/flow_definition_test.go

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ import (
1515
)
1616

1717
func TestCreateFlowDefinitionUnauthenticated(t *testing.T) {
18-
t.Parallel()
19-
2018
userSchema := "https://some-tenant.com/schemas/unknown-user-schema.yaml"
2119
userSchemaURI, err := url.Parse(userSchema)
2220
require.NoError(t, err)
@@ -49,12 +47,11 @@ func TestCreateFlowDefinitionUnauthenticated(t *testing.T) {
4947
}
5048

5149
func TestCreateFlowDefinition(t *testing.T) {
52-
t.Parallel()
5350
project, err := harness.EnsureProjectService(t).Create(t.Context(), nil)
5451
require.NoError(t, err)
55-
harness.CreateUserSchema(t, project.ID, harness.TestData.Schemas.CreateSchemaRequestUserSchema)
5652

57-
u := "https://raw.githubusercontent.com/zitadel/nextgen/refs/heads/main/api/openapi/endpoints/schemas/examples/user-schema-example.yaml"
53+
u := flowDefinitionSchemaURL(t)
54+
harness.EnsureUserSchemaWithID(t, project.ID, harness.TestData.Schemas.CreateSchemaRequestUserSchema, u)
5855
userSchemaURI, err := url.Parse(u)
5956
require.NoError(t, err)
6057

@@ -290,7 +287,6 @@ func TestCreateFlowDefinition(t *testing.T) {
290287
}
291288
for _, tt := range tests {
292289
t.Run(tt.name, func(t *testing.T) {
293-
t.Parallel()
294290
client := harness.EnsureAPIClient(t, project.ID)
295291
resp, err := client.CreateFlowDefinition(t.Context(), tt.req)
296292
assert.NoError(t, err)
@@ -365,8 +361,12 @@ func validSteps() []api.FlowDefinitionStep {
365361
}
366362
}
367363

364+
func flowDefinitionSchemaURL(t *testing.T) string {
365+
t.Helper()
366+
return "https://test.example.schemas.com/integration/" + url.PathEscape(t.Name()) + ".json"
367+
}
368+
368369
func TestGetFlowDefinitionUnauthenticated(t *testing.T) {
369-
t.Parallel()
370370
client := harness.EnsureAnonymousAPIClient(t)
371371
getResp, err := client.GetFlowDefinition(t.Context(), api.GetFlowDefinitionParams{
372372
ID: "flowDef_1234",
@@ -385,11 +385,10 @@ func TestGetFlowDefinitionUnauthenticated(t *testing.T) {
385385
}
386386

387387
func TestGetFlowDefinition(t *testing.T) {
388-
t.Parallel()
389388
project, err := harness.EnsureProjectService(t).Create(t.Context(), nil)
390389
require.NoError(t, err)
391-
harness.CreateUserSchema(t, project.ID, harness.TestData.Schemas.CreateSchemaRequestUserSchema)
392-
u := "https://raw.githubusercontent.com/zitadel/nextgen/refs/heads/main/api/openapi/endpoints/schemas/examples/user-schema-example.yaml"
390+
u := flowDefinitionSchemaURL(t)
391+
harness.EnsureUserSchemaWithID(t, project.ID, harness.TestData.Schemas.CreateSchemaRequestUserSchema, u)
393392
userSchemaURI, err := url.Parse(u)
394393
require.NoError(t, err)
395394

@@ -442,7 +441,6 @@ func TestGetFlowDefinition(t *testing.T) {
442441
}
443442
for _, tt := range tests {
444443
t.Run(tt.name, func(t *testing.T) {
445-
t.Parallel()
446444
client := harness.EnsureAPIClient(t, project.ID)
447445
resp, err := client.GetFlowDefinition(t.Context(), tt.req)
448446
assert.NoError(t, err)
@@ -452,8 +450,6 @@ func TestGetFlowDefinition(t *testing.T) {
452450
}
453451

454452
func TestListFlowDefinitionsUnauthenticated(t *testing.T) {
455-
t.Parallel()
456-
457453
client := harness.EnsureAnonymousAPIClient(t)
458454
getResp, err := client.ListFlowDefinitions(t.Context(), api.ListFlowDefinitionsParams{
459455
ProjectID: "proj_1234",
@@ -471,19 +467,18 @@ func TestListFlowDefinitionsUnauthenticated(t *testing.T) {
471467
}
472468

473469
func TestListFlowDefinitions(t *testing.T) {
474-
t.Parallel()
475470
project1, err := harness.EnsureProjectService(t).Create(t.Context(), nil)
476471
require.NoError(t, err)
477-
harness.CreateUserSchema(t, project1.ID, harness.TestData.Schemas.CreateSchemaRequestUserSchema)
478472

479473
project2, err := harness.EnsureProjectService(t).Create(t.Context(), nil)
480474
require.NoError(t, err)
481-
harness.CreateUserSchema(t, project2.ID, harness.TestData.Schemas.CreateSchemaRequestUserSchema)
482475

483476
project3, err := harness.EnsureProjectService(t).Create(t.Context(), nil)
484477
require.NoError(t, err)
485478

486-
u := "https://raw.githubusercontent.com/zitadel/nextgen/refs/heads/main/api/openapi/endpoints/schemas/examples/user-schema-example.yaml"
479+
u := flowDefinitionSchemaURL(t)
480+
harness.EnsureUserSchemaWithID(t, project1.ID, harness.TestData.Schemas.CreateSchemaRequestUserSchema, u)
481+
harness.EnsureUserSchemaWithID(t, project2.ID, harness.TestData.Schemas.CreateSchemaRequestUserSchema, u)
487482
userSchemaURI, err := url.Parse(u)
488483
require.NoError(t, err)
489484

@@ -666,7 +661,6 @@ func TestListFlowDefinitions(t *testing.T) {
666661
}
667662
for _, tt := range tests {
668663
t.Run(tt.name, func(t *testing.T) {
669-
t.Parallel()
670664
client := harness.EnsureAPIClient(t, project1.ID)
671665
resp, err := client.ListFlowDefinitions(t.Context(), tt.req)
672666
assert.NoError(t, err)
@@ -699,7 +693,6 @@ func TestListFlowDefinitions(t *testing.T) {
699693
}
700694

701695
func TestDeleteFlowDefinitionUnauthenticated(t *testing.T) {
702-
t.Parallel()
703696
client := harness.EnsureAnonymousAPIClient(t)
704697
resp, err := client.DeleteFlowDefinition(t.Context(), api.DeleteFlowDefinitionParams{
705698
ID: "flowDef_1234",
@@ -717,11 +710,10 @@ func TestDeleteFlowDefinitionUnauthenticated(t *testing.T) {
717710
}
718711

719712
func TestDeleteFlowDefinition(t *testing.T) {
720-
t.Parallel()
721713
project, err := harness.EnsureProjectService(t).Create(t.Context(), nil)
722714
require.NoError(t, err)
723-
harness.CreateUserSchema(t, project.ID, harness.TestData.Schemas.CreateSchemaRequestUserSchema)
724-
u := "https://raw.githubusercontent.com/zitadel/nextgen/refs/heads/main/api/openapi/endpoints/schemas/examples/user-schema-example.yaml"
715+
u := flowDefinitionSchemaURL(t)
716+
harness.EnsureUserSchemaWithID(t, project.ID, harness.TestData.Schemas.CreateSchemaRequestUserSchema, u)
725717
userSchemaURI, err := url.Parse(u)
726718
require.NoError(t, err)
727719

@@ -776,7 +768,6 @@ func TestDeleteFlowDefinition(t *testing.T) {
776768
}
777769
for _, tt := range tests {
778770
t.Run(tt.name, func(t *testing.T) {
779-
t.Parallel()
780771
client := harness.EnsureAPIClient(t, project.ID)
781772
resp, err := client.DeleteFlowDefinition(t.Context(), tt.req)
782773
assert.NoError(t, err)

internal/api/integration_test/helpers/auth_attempt.go

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,47 @@ import (
1010

1111
func (h *Harness) EnsureAuthAttemptService(t *testing.T) service.AuthAttemptService {
1212
t.Helper()
13+
h.mu.Lock()
14+
svc := h.AuthAttemptService
15+
h.mu.Unlock()
16+
if svc != nil {
17+
return svc
18+
}
19+
svc = service.NewAuthAttemptService(
20+
h.EnsureDBPool(t),
21+
h.EnsureAuthAttemptRepo(t),
22+
h.EnsureSessionRepo(t),
23+
h.EnsureProjectRepo(t),
24+
h.EnsureUserRepo(t),
25+
h.EnsureUserPasswordRepo(t),
26+
h.EnsureUserPasskeyRepo(t),
27+
h.EnsureHashVerifier(t),
28+
)
29+
h.mu.Lock()
1330
if h.AuthAttemptService == nil {
14-
h.AuthAttemptService = service.NewAuthAttemptService(
15-
h.EnsureDBPool(t),
16-
h.EnsureAuthAttemptRepo(t),
17-
h.EnsureSessionRepo(t),
18-
h.EnsureProjectRepo(t),
19-
h.EnsureUserRepo(t),
20-
h.EnsureUserPasswordRepo(t),
21-
h.EnsureUserPasskeyRepo(t),
22-
h.EnsureHashVerifier(t),
23-
)
31+
h.AuthAttemptService = svc
2432
}
25-
return h.AuthAttemptService
33+
svc = h.AuthAttemptService
34+
h.mu.Unlock()
35+
return svc
2636
}
2737

2838
func (h *Harness) EnsureAuthAttemptRepo(t *testing.T) domain.AuthAttemptRepository {
2939
t.Helper()
40+
h.mu.Lock()
41+
repo := h.AuthAttemptRepo
42+
h.mu.Unlock()
43+
if repo != nil {
44+
return repo
45+
}
46+
repo = repository.NewAuthAttemptRepository(
47+
h.EnsureDBPool(t),
48+
)
49+
h.mu.Lock()
3050
if h.AuthAttemptRepo == nil {
31-
h.AuthAttemptRepo = repository.NewAuthAttemptRepository(
32-
h.EnsureDBPool(t),
33-
)
51+
h.AuthAttemptRepo = repo
3452
}
35-
return h.AuthAttemptRepo
53+
repo = h.AuthAttemptRepo
54+
h.mu.Unlock()
55+
return repo
3656
}

internal/api/integration_test/helpers/client.go

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,56 +11,101 @@ import (
1111

1212
func (h *Harness) EnsureAPIClient(t *testing.T, projectID string) *api.Client {
1313
t.Helper()
14-
serv := h.EnsureTestServer(t)
15-
if h.apiClients == nil {
16-
h.apiClients = make(map[string]*api.Client)
17-
}
18-
if client, ok := h.apiClients[projectID]; ok {
19-
return client
14+
h.mu.Lock()
15+
if h.apiClients != nil {
16+
if client, ok := h.apiClients[projectID]; ok {
17+
h.mu.Unlock()
18+
return client
19+
}
2020
}
21+
h.mu.Unlock()
22+
23+
serv := h.EnsureTestServer(t)
2124
client, err := api.NewClient(
2225
serv.URL,
2326
h.EnsureFakeSecuritySource(t, projectID),
2427
)
2528
require.NoError(t, err)
29+
h.mu.Lock()
30+
if h.apiClients == nil {
31+
h.apiClients = make(map[string]*api.Client)
32+
}
33+
if existing, ok := h.apiClients[projectID]; ok {
34+
h.mu.Unlock()
35+
return existing
36+
}
2637
h.apiClients[projectID] = client
38+
h.mu.Unlock()
2739
return client
2840
}
2941

3042
func (h *Harness) EnsureAnonymousAPIClient(t *testing.T) *api.Client {
3143
t.Helper()
44+
h.mu.Lock()
45+
client := h.anonymousClient
46+
h.mu.Unlock()
47+
if client != nil {
48+
return client
49+
}
50+
3251
serv := h.EnsureTestServer(t)
52+
client, err := api.NewClient(
53+
serv.URL,
54+
h.EnsureAnonymousSecuritySource(t),
55+
)
56+
require.NoError(t, err)
57+
h.mu.Lock()
3358
if h.anonymousClient == nil {
34-
client, err := api.NewClient(
35-
serv.URL,
36-
h.EnsureAnonymousSecuritySource(t),
37-
)
38-
require.NoError(t, err)
3959
h.anonymousClient = client
4060
}
41-
return h.anonymousClient
61+
client = h.anonymousClient
62+
h.mu.Unlock()
63+
return client
4264
}
4365

4466
func (h *Harness) EnsureFakeSecuritySource(t *testing.T, projectID string) *FakeSecuritySource {
4567
t.Helper()
68+
h.mu.Lock()
69+
if h.fakeSecuritySources != nil {
70+
if source, ok := h.fakeSecuritySources[projectID]; ok {
71+
h.mu.Unlock()
72+
return source
73+
}
74+
}
75+
h.mu.Unlock()
76+
77+
source := &FakeSecuritySource{
78+
projectID: projectID,
79+
}
80+
h.mu.Lock()
4681
if h.fakeSecuritySources == nil {
4782
h.fakeSecuritySources = make(map[string]*FakeSecuritySource)
4883
}
4984
if source, ok := h.fakeSecuritySources[projectID]; ok {
85+
h.mu.Unlock()
5086
return source
5187
}
52-
h.fakeSecuritySources[projectID] = &FakeSecuritySource{
53-
projectID: projectID,
54-
}
55-
return h.fakeSecuritySources[projectID]
88+
h.fakeSecuritySources[projectID] = source
89+
h.mu.Unlock()
90+
return source
5691
}
5792

5893
func (h *Harness) EnsureAnonymousSecuritySource(t *testing.T) *FakeSecuritySource {
5994
t.Helper()
95+
h.mu.Lock()
96+
source := h.anonymousSecuritySource
97+
h.mu.Unlock()
98+
if source != nil {
99+
return source
100+
}
101+
source = &FakeSecuritySource{}
102+
h.mu.Lock()
60103
if h.anonymousSecuritySource == nil {
61-
h.anonymousSecuritySource = &FakeSecuritySource{}
104+
h.anonymousSecuritySource = source
62105
}
63-
return h.anonymousSecuritySource
106+
source = h.anonymousSecuritySource
107+
h.mu.Unlock()
108+
return source
64109
}
65110

66111
type FakeSecuritySource struct {

0 commit comments

Comments
 (0)