Files
BackupX/server/internal/service/api_key_service_test.go
T
Awuqing c06bbec383 refactor: simplify architecture and harden lifecycle
Remove obsolete implementations, centralize background task ownership and terminal-state recovery, consolidate frontend routing and log streaming, and enforce project-wide verification in CI.
2026-08-26 11:32:00 +08:00

115 lines
3.1 KiB
Go

package service
import (
"context"
"path/filepath"
"strings"
"testing"
"time"
"backupx/server/internal/config"
"backupx/server/internal/database"
"backupx/server/internal/logger"
"backupx/server/internal/model"
"backupx/server/internal/repository"
)
func newApiKeyTestService(t *testing.T) *ApiKeyService {
t.Helper()
log, err := logger.New(config.LogConfig{Level: "error"})
if err != nil {
t.Fatalf("logger.New: %v", err)
}
db, err := database.Open(config.DatabaseConfig{Path: filepath.Join(t.TempDir(), "backupx.db")}, log)
if err != nil {
t.Fatalf("database.Open: %v", err)
}
closeTestDatabase(t, db)
return NewApiKeyService(repository.NewApiKeyRepository(db))
}
func TestApiKeyService_CreateAndAuthenticate(t *testing.T) {
svc := newApiKeyTestService(t)
ctx := context.Background()
result, err := svc.Create(ctx, "tester", ApiKeyCreateInput{
Name: "ci",
Role: model.UserRoleOperator,
TTLHours: 0,
})
if err != nil {
t.Fatalf("Create: %v", err)
}
if !strings.HasPrefix(result.PlainKey, ApiKeyPrefix) {
t.Fatalf("expected plain key with prefix %s, got %s", ApiKeyPrefix, result.PlainKey)
}
if result.ApiKey.Role != model.UserRoleOperator {
t.Fatalf("role not preserved")
}
subject, role, err := svc.Authenticate(ctx, result.PlainKey)
if err != nil {
t.Fatalf("Authenticate: %v", err)
}
if role != model.UserRoleOperator {
t.Fatalf("expected operator role, got %s", role)
}
if !strings.HasPrefix(subject, "api_key:") {
t.Fatalf("expected subject to start with api_key:, got %s", subject)
}
}
func TestApiKeyService_AuthenticateRejectsInvalid(t *testing.T) {
svc := newApiKeyTestService(t)
ctx := context.Background()
// 格式错误(无 bax_ 前缀)
if _, _, err := svc.Authenticate(ctx, "invalid-without-prefix"); err == nil {
t.Fatalf("expected error for missing prefix")
}
// 格式正确但不存在
if _, _, err := svc.Authenticate(ctx, "bax_"+strings.Repeat("0", 48)); err == nil {
t.Fatalf("expected error for unknown key")
}
}
func TestApiKeyService_AuthenticateRejectsExpired(t *testing.T) {
svc := newApiKeyTestService(t)
ctx := context.Background()
result, err := svc.Create(ctx, "tester", ApiKeyCreateInput{
Name: "ci-expired",
Role: model.UserRoleViewer,
TTLHours: 1,
})
if err != nil {
t.Fatalf("Create: %v", err)
}
// 手动把 expiresAt 设到过去
key, _ := svc.repo.FindByID(ctx, result.ApiKey.ID)
past := time.Now().UTC().Add(-time.Hour)
key.ExpiresAt = &past
if err := svc.repo.Update(ctx, key); err != nil {
t.Fatalf("Update: %v", err)
}
if _, _, err := svc.Authenticate(ctx, result.PlainKey); err == nil {
t.Fatalf("expected error for expired key")
}
}
func TestApiKeyService_AuthenticateRejectsDisabled(t *testing.T) {
svc := newApiKeyTestService(t)
ctx := context.Background()
result, err := svc.Create(ctx, "tester", ApiKeyCreateInput{Name: "disabled", Role: "admin"})
if err != nil {
t.Fatalf("Create: %v", err)
}
if err := svc.ToggleDisabled(ctx, result.ApiKey.ID, true); err != nil {
t.Fatalf("ToggleDisabled: %v", err)
}
if _, _, err := svc.Authenticate(ctx, result.PlainKey); err == nil {
t.Fatalf("expected error for disabled key")
}
}