iop/apps/edge/internal/bootstrap/credential_plane_mode_test.go
toki 4c8441e6c9 feat(credential): Provider Credential Slot 라우팅을 구현한다
사용자별 credential 저장, lease, projection, runtime 전달과 OpenAI-compatible 계약 및 검증 근거를 함께 반영한다.
2026-08-02 09:10:11 +09:00

111 lines
4 KiB
Go

package bootstrap
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"iop/apps/edge/internal/configrefresh"
)
const legacyCredentialModeYAML = `
server:
listen: "127.0.0.1:9090"
credential_plane:
enabled: false
`
const managedCredentialModeYAML = `
server:
listen: "127.0.0.1:9090"
tls:
enabled: true
cert: "/cert"
key: "/key"
ca: "/ca"
peer_role: "node"
control_plane:
enabled: true
wire_addr: "control-plane:19081"
tls:
enabled: true
cert: "/cert"
key: "/key"
ca: "/ca"
server_name: "control-plane"
peer_role: "control-plane"
credential_plane:
enabled: true
lease_ttl_seconds: 30
lease_cache_size: 256
`
func writeCredentialModeConfig(t *testing.T, name, body string) string {
t.Helper()
path := filepath.Join(t.TempDir(), name)
if err := os.WriteFile(path, []byte(body), 0o600); err != nil {
t.Fatal(err)
}
return path
}
func TestCredentialModeRefreshAndRollbackRequireRestartWithoutPartialApply(t *testing.T) {
managedEdgeKeyChanged := strings.Replace(managedCredentialModeYAML, " key: \"/key\"\n ca: \"/ca\"\n peer_role: \"node\"", " key: \"/rotated-edge-key\"\n ca: \"/ca\"\n peer_role: \"node\"", 1)
managedControlPlaneKeyChanged := strings.Replace(managedCredentialModeYAML, " key: \"/key\"\n ca: \"/ca\"\n server_name:", " key: \"/rotated-control-plane-key\"\n ca: \"/ca\"\n server_name:", 1)
managedOpenAI := managedCredentialModeYAML + `
openai:
enabled: true
provider_id: "provider-fixture"
tls:
enabled: true
cert: "/openai-cert"
key: "/openai-key"
`
for _, tc := range []struct {
name string
currentBody string
candidateBody string
expectedPath string
}{
{name: "legacy to managed", currentBody: legacyCredentialModeYAML, candidateBody: managedCredentialModeYAML, expectedPath: "credential_plane"},
{name: "managed rollback to legacy", currentBody: managedCredentialModeYAML, candidateBody: legacyCredentialModeYAML, expectedPath: "credential_plane"},
{name: "legacy source change", currentBody: legacyCredentialModeYAML, candidateBody: legacyCredentialModeYAML + "\nopenai:\n bearer_token: legacy-fixture\n", expectedPath: "openai"},
{name: "managed lease setting change", currentBody: managedCredentialModeYAML, candidateBody: strings.Replace(managedCredentialModeYAML, "lease_ttl_seconds: 30", "lease_ttl_seconds: 31", 1), expectedPath: "credential_plane"},
{name: "managed edge TLS key rotation", currentBody: managedCredentialModeYAML, candidateBody: managedEdgeKeyChanged, expectedPath: "tls"},
{name: "managed Control Plane TLS key rotation", currentBody: managedCredentialModeYAML, candidateBody: managedControlPlaneKeyChanged, expectedPath: "control_plane"},
{name: "managed OpenAI TLS key rotation", currentBody: managedOpenAI, candidateBody: strings.Replace(managedOpenAI, "/openai-key", "/rotated-openai-key", 1), expectedPath: "openai"},
} {
t.Run(tc.name, func(t *testing.T) {
currentPath := writeCredentialModeConfig(t, "current.yaml", tc.currentBody)
candidatePath := writeCredentialModeConfig(t, "candidate.yaml", tc.candidateBody)
current, err := configrefresh.LoadCandidate(currentPath)
if err != nil {
t.Fatalf("load current: %v", err)
}
runtime := &Runtime{Cfg: current}
result, err := runtime.RefreshConfig(context.Background(), configrefresh.Request{
Mode: configrefresh.ModeApply, ConfigPath: candidatePath, RequestID: "credential-mode-transition",
})
if err != nil {
t.Fatalf("RefreshConfig: %v", err)
}
if result.Status != configrefresh.StatusRestartRequired {
t.Fatalf("status = %q, want restart_required: %+v", result.Status, result.Changes)
}
found := false
for _, change := range result.Changes {
if change.Path == tc.expectedPath && change.Class == configrefresh.StatusRestartRequired {
found = true
}
}
if !found {
t.Fatalf("restart path %q missing: %+v", tc.expectedPath, result.Changes)
}
if runtime.Cfg != current {
t.Fatal("restart-required credential mode was partially applied")
}
})
}
}