fix(test): resolve nil-pointer panic in TestCommandSide_ChangeUserHuman (#11695)

# Which Problems Are Solved

1. `nx run @zitadel/api:test-unit` panics in
`TestCommandSide_ChangeUserHuman` due to two test cases (added in
0261536) missing the required `loginPaths` field. Since the field type
is `func(*testing.T) LoginPaths`, its zero value is `nil`, and calling
it causes a SIGSEGV.

2. Three targets in `apps/api/project.json` (`test-unit`, `build`,
`build-linux`) were silently non-cacheable because NX does not merge
`cache: true` from `targetDefaults` when a project-level target
overrides other properties like `dependsOn` or `inputs`.

3. `TestServer_AuthorizeOrDenyDeviceAuthorization` integration test is
flaky — it uses hardcoded `5*time.Second` timeouts for `EventuallyWithT`
polling, while the rest of the file uses
`WaitForAndTickWithMaxDuration(ctx, time.Minute)`. Under CI load, 5
seconds is insufficient and the empty ID cascades into a validation
error.

# How the Problems Are Solved

**Test panic fix:**
- Added missing `loginPaths: expectLoginPathsNoCall` to both broken test
cases ("change human email verified (self-management), not allowed" and
"change human phone verified (self-management), not allowed").

**NX cache fix:**
- Added explicit `"cache": true` to `test-unit`, `build`, and
`build-linux` targets in `apps/api/project.json`.
- Verified with `pnpm nx show project @zitadel/api --json` that all
three targets now resolve with `cache: true`.

**Integration test flakiness fix:**
- Replaced all 6 hardcoded `assert.EventuallyWithT(t, ...,
5*time.Second, 100*time.Millisecond)` calls in
`TestServer_AuthorizeOrDenyDeviceAuthorization` with
`require.EventuallyWithT(t, ..., retryDuration, tick)` using
`integration.WaitForAndTickWithMaxDuration(CTXLoginClient,
time.Minute)`.
- Changed from `assert` (non-fatal) to `require` (fatal) so timeout
failures stop the test immediately instead of cascading with empty IDs.

# Additional Context

- The broken unit test landed on main because CI skips `lint_test_build`
on pushes to main (`if: github.ref != 'refs/heads/main'`). A follow-up
issue was created: #11696.
- The integration test flakiness was missed by the previous fix in
#10752.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Livio Spring <livio@zitadel.com>
This commit is contained in:
Florian Forster
2026-02-26 05:47:01 +00:00
committed by GitHub
co-authored by Copilot Livio Spring
parent f5020f3873
commit e9e6ad4e17
3 changed files with 23 additions and 12 deletions
+3
View File
@@ -66,6 +66,7 @@
"generate",
"build-console"
],
"cache": true,
"command": "bash -c 'CGO_ENABLED=0 go build -o .artifacts/bin/$(go env GOOS)/$(go env GOARCH)/zitadel.local -ldflags=\"-s -w\"'",
"inputs": [
{
@@ -89,6 +90,7 @@
"generate",
"build-console"
],
"cache": true,
"command": "bash -c 'CGO_ENABLED=0 GOOS=linux go build -o .artifacts/bin/linux/$(go env GOARCH)/zitadel.local -ldflags=\"-s -w\"'",
"inputs": [
{
@@ -161,6 +163,7 @@
"dependsOn": [
"generate"
],
"cache": true,
"command": "go test -race -coverprofile=profile.api.test-unit.cov -coverpkg=./internal/...,./backend/... ./...",
"inputs": [
"sources",
@@ -752,13 +752,14 @@ func TestServer_AuthorizeOrDenyDeviceAuthorization(t *testing.T) {
req, err := Instance.CreateDeviceAuthorizationRequest(CTXLoginClient, client.GetClientId(), "openid")
require.NoError(t, err)
var id string
assert.EventuallyWithT(t, func(collectT *assert.CollectT) {
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTXLoginClient, time.Minute)
require.EventuallyWithT(t, func(collectT *assert.CollectT) {
resp, err := Instance.Client.OIDCv2.GetDeviceAuthorizationRequest(CTXLoginClient, &oidc_pb.GetDeviceAuthorizationRequestRequest{
UserCode: req.UserCode,
})
assert.NoError(collectT, err)
id = resp.GetDeviceAuthorizationRequest().GetId()
}, 5*time.Second, 100*time.Millisecond)
}, retryDuration, tick)
return id
}(),
Decision: &oidc_pb.AuthorizeOrDenyDeviceAuthorizationRequest_Session{
@@ -778,13 +779,14 @@ func TestServer_AuthorizeOrDenyDeviceAuthorization(t *testing.T) {
req, err := Instance.CreateDeviceAuthorizationRequest(CTXLoginClient, client.GetClientId(), "openid")
require.NoError(t, err)
var id string
assert.EventuallyWithT(t, func(collectT *assert.CollectT) {
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTXLoginClient, time.Minute)
require.EventuallyWithT(t, func(collectT *assert.CollectT) {
resp, err := Instance.Client.OIDCv2.GetDeviceAuthorizationRequest(CTXLoginClient, &oidc_pb.GetDeviceAuthorizationRequestRequest{
UserCode: req.UserCode,
})
assert.NoError(collectT, err)
id = resp.GetDeviceAuthorizationRequest().GetId()
}, 5*time.Second, 100*time.Millisecond)
}, retryDuration, tick)
return id
}(),
Decision: &oidc_pb.AuthorizeOrDenyDeviceAuthorizationRequest_Session{
@@ -804,13 +806,14 @@ func TestServer_AuthorizeOrDenyDeviceAuthorization(t *testing.T) {
req, err := Instance.CreateDeviceAuthorizationRequest(CTXLoginClient, client.GetClientId(), "openid")
require.NoError(t, err)
var id string
assert.EventuallyWithT(t, func(collectT *assert.CollectT) {
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTXLoginClient, time.Minute)
require.EventuallyWithT(t, func(collectT *assert.CollectT) {
resp, err := Instance.Client.OIDCv2.GetDeviceAuthorizationRequest(CTXLoginClient, &oidc_pb.GetDeviceAuthorizationRequestRequest{
UserCode: req.UserCode,
})
assert.NoError(collectT, err)
id = resp.GetDeviceAuthorizationRequest().GetId()
}, 5*time.Second, 100*time.Millisecond)
}, retryDuration, tick)
return id
}(),
Decision: &oidc_pb.AuthorizeOrDenyDeviceAuthorizationRequest_Deny{},
@@ -826,13 +829,14 @@ func TestServer_AuthorizeOrDenyDeviceAuthorization(t *testing.T) {
req, err := Instance.CreateDeviceAuthorizationRequest(CTXLoginClient, client.GetClientId(), "openid")
require.NoError(t, err)
var id string
assert.EventuallyWithT(t, func(collectT *assert.CollectT) {
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTXLoginClient, time.Minute)
require.EventuallyWithT(t, func(collectT *assert.CollectT) {
resp, err := Instance.Client.OIDCv2.GetDeviceAuthorizationRequest(CTXLoginClient, &oidc_pb.GetDeviceAuthorizationRequestRequest{
UserCode: req.UserCode,
})
assert.NoError(collectT, err)
id = resp.GetDeviceAuthorizationRequest().GetId()
}, 5*time.Second, 100*time.Millisecond)
}, retryDuration, tick)
return id
}(),
Decision: &oidc_pb.AuthorizeOrDenyDeviceAuthorizationRequest_Deny{},
@@ -848,13 +852,14 @@ func TestServer_AuthorizeOrDenyDeviceAuthorization(t *testing.T) {
req, err := Instance.CreateDeviceAuthorizationRequest(CTXLoginClient, client.GetClientId(), "openid")
require.NoError(t, err)
var id string
assert.EventuallyWithT(t, func(collectT *assert.CollectT) {
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTXLoginClient, time.Minute)
require.EventuallyWithT(t, func(collectT *assert.CollectT) {
resp, err := Instance.Client.OIDCv2.GetDeviceAuthorizationRequest(CTXLoginClient, &oidc_pb.GetDeviceAuthorizationRequestRequest{
UserCode: req.UserCode,
})
assert.NoError(collectT, err)
id = resp.GetDeviceAuthorizationRequest().GetId()
}, 5*time.Second, 100*time.Millisecond)
}, retryDuration, tick)
return id
}(),
Decision: &oidc_pb.AuthorizeOrDenyDeviceAuthorizationRequest_Session{
@@ -874,13 +879,14 @@ func TestServer_AuthorizeOrDenyDeviceAuthorization(t *testing.T) {
req, err := Instance.CreateDeviceAuthorizationRequest(CTXLoginClient, client.GetClientId(), "openid")
require.NoError(t, err)
var id string
assert.EventuallyWithT(t, func(collectT *assert.CollectT) {
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTXLoginClient, time.Minute)
require.EventuallyWithT(t, func(collectT *assert.CollectT) {
resp, err := Instance.Client.OIDCv2.GetDeviceAuthorizationRequest(CTXLoginClient, &oidc_pb.GetDeviceAuthorizationRequestRequest{
UserCode: req.UserCode,
})
assert.NoError(collectT, err)
id = resp.GetDeviceAuthorizationRequest().GetId()
}, 5*time.Second, 100*time.Millisecond)
}, retryDuration, tick)
return id
}(),
Decision: &oidc_pb.AuthorizeOrDenyDeviceAuthorizationRequest_Session{
+2
View File
@@ -2711,6 +2711,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
),
),
checkPermission: newMockPermissionCheckNotAllowed(),
loginPaths: expectLoginPathsNoCall,
tarpit: expectTarpit(0),
},
args: args{
@@ -3060,6 +3061,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) {
),
),
checkPermission: newMockPermissionCheckNotAllowed(),
loginPaths: expectLoginPathsNoCall,
tarpit: expectTarpit(0),
},
args: args{