Scert/backend/internal/repository/server_repository_test.go

167 lines
6.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package repository
import (
"context"
"path/filepath"
"testing"
"git.pcux.cn/xuans/Scert/backend/internal/config"
"git.pcux.cn/xuans/Scert/backend/internal/database"
"git.pcux.cn/xuans/Scert/backend/internal/deployment"
)
func TestServerRepositoryCRUDAndDomainAssociations(t *testing.T) {
dir := t.TempDir()
cfg := config.Default(dir).Database
cfg.DSN = filepath.Join(dir, "data", "servers.db")
db, err := database.Open(context.Background(), cfg)
if err != nil {
t.Fatal(err)
}
defer db.Close()
if err := database.Migrate(context.Background(), db.DB, "sqlite"); err != nil {
t.Fatal(err)
}
repo := NewSQLServerRepository(db.DB)
server := deployment.Server{
ID: "server-1", Name: "生产服务器", Address: "192.0.2.10", Port: 22, Username: "deploy",
PrivateKeyRef: "secret://ssh/server-1", HostKeyFingerprint: "SHA256:test",
CertificatePath: "/etc/scert/cert.pem", PrivateKeyPath: "/etc/scert/key.pem",
CertificateChainPath: "/etc/scert/fullchain.pem", WebServer: deployment.WebServerNginx,
}
if err := repo.CreateServer(context.Background(), server); err != nil {
t.Fatalf("创建服务器失败:%v", err)
}
loaded, err := repo.GetServerByID(context.Background(), server.ID)
if err != nil {
t.Fatalf("读取服务器失败:%v", err)
}
if loaded.Name != server.Name || loaded.ValidationCommand[0] != "nginx" || loaded.ValidationCommand[1] != "-t" || loaded.ReloadCommand[0] != "nginx" || loaded.CreatedAt.IsZero() {
t.Fatalf("服务器内容错误:%+v", loaded)
}
server.Name = "生产服务器-更新"
server.WebServer = deployment.WebServerCaddy
server.ReloadCommand = nil
if err := repo.UpdateServer(context.Background(), server); err != nil {
t.Fatalf("更新服务器失败:%v", err)
}
updated, err := repo.GetServerByID(context.Background(), server.ID)
if err != nil {
t.Fatalf("load updated server: %v", err)
}
if updated.CreatedAt.IsZero() || updated.UpdatedAt.IsZero() || updated.UpdatedAt.Before(updated.CreatedAt) {
t.Fatalf("updated server timestamps are invalid: %+v", updated)
}
if err := repo.ReplaceDomainServers(context.Background(), "domain-1", []string{server.ID, server.ID}); err != nil {
t.Fatalf("保存域名服务器关联失败:%v", err)
}
associated, err := repo.ListServersByDomainID(context.Background(), "domain-1")
if err != nil || len(associated) != 1 || associated[0].WebServer != deployment.WebServerCaddy {
t.Fatalf("域名服务器关联错误:%v %+v", err, associated)
}
servers, err := repo.ListServers(context.Background())
if err != nil || len(servers) != 1 {
t.Fatalf("服务器列表错误:%v %+v", err, servers)
}
if err := repo.DeleteServer(context.Background(), server.ID); err != nil {
t.Fatalf("删除服务器失败:%v", err)
}
associated, err = repo.ListServersByDomainID(context.Background(), "domain-1")
if err != nil {
t.Fatalf("读取删除后的域名服务器关联失败:%v", err)
}
if len(associated) != 0 {
t.Fatalf("删除服务器后不应保留悬空关联:%+v", associated)
}
}
func TestServerRepositoryRejectsInvalidServer(t *testing.T) {
db, err := database.Open(context.Background(), config.DatabaseConfig{
Driver: "sqlite", DSN: ":memory:", MaxOpenConns: 1, MaxIdleConns: 1,
})
if err != nil {
t.Fatal(err)
}
defer db.Close()
if err := database.Migrate(context.Background(), db.DB, "sqlite"); err != nil {
t.Fatal(err)
}
if err := NewSQLServerRepository(db.DB).CreateServer(context.Background(), deployment.Server{}); err == nil {
t.Fatal("无效服务器配置应被拒绝")
}
}
func TestServerRepositoryReportsPrivateKeyReferenceUsage(t *testing.T) {
db := openServerTestDatabase(t)
defer db.Close()
repo := NewSQLServerRepository(db.DB)
server := validTestServer()
server.ID = "server-private-key-usage"
server.PrivateKeyRef = "secret://ssh/used-key"
if err := repo.CreateServer(t.Context(), server); err != nil {
t.Fatalf("创建测试服务器失败:%v", err)
}
used, err := repo.IsPrivateKeyRefUsed(t.Context(), server.PrivateKeyRef)
if err != nil || !used {
t.Fatalf("未识别已占用的 SSH 私钥引用:used=%t err=%v", used, err)
}
used, err = repo.IsPrivateKeyRefUsed(t.Context(), "secret://ssh/not-used")
if err != nil || used {
t.Fatalf("错误识别未占用的 SSH 私钥引用:used=%t err=%v", used, err)
}
}
func TestReplaceDomainServersRejectsUnknownServerWithoutChangingAssociations(t *testing.T) {
dir := t.TempDir()
cfg := config.Default(dir).Database
cfg.DSN = filepath.Join(dir, "data", "association.db")
db, err := database.Open(context.Background(), cfg)
if err != nil {
t.Fatal(err)
}
defer db.Close()
if err := database.Migrate(context.Background(), db.DB, "sqlite"); err != nil {
t.Fatal(err)
}
repo := NewSQLServerRepository(db.DB)
server := deployment.Server{ID: "server-1", Name: "服务器", Address: "192.0.2.20", Username: "deploy", PrivateKeyRef: "secret://ssh/1", HostKeyFingerprint: "SHA256:test", CertificatePath: "/etc/scert/cert.pem", PrivateKeyPath: "/etc/scert/key.pem", CertificateChainPath: "/etc/scert/fullchain.pem", WebServer: deployment.WebServerNginx}
if err := repo.CreateServer(context.Background(), server); err != nil {
t.Fatal(err)
}
if err := repo.ReplaceDomainServers(context.Background(), "domain-1", []string{server.ID}); err != nil {
t.Fatal(err)
}
if err := repo.ReplaceDomainServers(context.Background(), "domain-1", []string{"missing"}); err == nil {
t.Fatal("未知服务器应被拒绝")
}
items, err := repo.ListServersByDomainID(context.Background(), "domain-1")
if err != nil || len(items) != 1 || items[0].ID != server.ID {
t.Fatalf("失败后原关联应保持不变:%v %+v", err, items)
}
}
func openServerTestDatabase(t *testing.T) *database.Database {
t.Helper()
directory := t.TempDir()
cfg := config.Default(directory).Database
cfg.DSN = filepath.Join(directory, "data", "server-test.db")
db, err := database.Open(t.Context(), cfg)
if err != nil {
t.Fatalf("打开服务器测试数据库失败:%v", err)
}
if err := database.Migrate(t.Context(), db.DB, "sqlite"); err != nil {
_ = db.Close()
t.Fatalf("迁移服务器测试数据库失败:%v", err)
}
return db
}
func validTestServer() deployment.Server {
return deployment.Server{
Name: "测试服务器", Address: "192.0.2.70", Port: 22, Username: "deploy",
PrivateKeyRef: "secret://ssh/test", HostKeyFingerprint: "SHA256:test",
CertificatePath: "/etc/scert/cert.pem", PrivateKeyPath: "/etc/scert/key.pem",
CertificateChainPath: "/etc/scert/fullchain.pem", WebServer: deployment.WebServerNginx,
}
}