mirror of
https://github.com/Awuqing/BackupX.git
synced 2026-08-28 19:47:27 +08:00
Remove obsolete implementations, centralize background task ownership and terminal-state recovery, consolidate frontend routing and log streaming, and enforce project-wide verification in CI.
115 lines
3.1 KiB
Go
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")
|
|
}
|
|
}
|