Files
LinkPort/internal/store/assets_backup_test.go
T
曾志威 9da95beb42
continuous-integration/drone Build is failing
feat: refine management UI, connection diagnostics and local startup
Migrate the workspace to React and Ant Design, improve account forms and responsive layouts, support outbound diagnostics and SSH fingerprint confirmation, and persist the management startup port. Preserve database migrations, remove S3 settings storage, and improve gateway audit handling.
2026-10-13 19:43:23 +08:00

121 lines
3.9 KiB
Go

package store
import (
"bytes"
"encoding/json"
"errors"
"linkport/internal/model"
"strings"
"testing"
)
func backupAsset() model.Asset {
return model.Asset{ID: "cluster-asset", Name: "cluster", Type: "kubeconfig", Content: `apiVersion: v1
kind: Config
current-context: test
clusters:
- name: cluster
cluster:
server: https://cluster.invalid
contexts:
- name: test
context:
cluster: cluster
user: user
users:
- name: user
user:
token: asset-private-token
`}
}
func TestAssetBackupRoundTripAndRedaction(t *testing.T) {
s := configStore(t)
a := backupAsset()
o := model.OutboundProfile{ID: "kube", Name: "kube", Outbound: model.Outbound{Mode: "k8s", Resource: "pod/db", KubeConfigAssetID: a.ID}}
in := Bundle{Version: 2, Assets: []model.Asset{a}, Outbounds: []model.OutboundProfile{o}}
if err := s.RestoreBundle(in); err != nil {
t.Fatal(err)
}
template, err := s.SaveTemplate("asset template", in)
if err != nil {
t.Fatal(err)
}
raw, _ := json.Marshal(template)
if bytes.Contains(raw, []byte("asset-private-token")) || a.Content == "" {
t.Fatal("template leaked or mutated asset")
}
backup, err := s.Backup("fixture-long-passphrase")
if err != nil {
t.Fatal(err)
}
restored, err := OpenBackup(backup, "fixture-long-passphrase")
if err != nil || len(restored.Assets) != 1 || restored.Assets[0].Content != a.Content || len(restored.Outbounds) != 1 || restored.Outbounds[0].Outbound.KubeConfigAssetID != a.ID {
t.Fatal("backup lost asset content or reference", err)
}
dest := configStore(t)
if err := dest.RestoreBundle(restored); err != nil {
t.Fatal("cannot restore asset and outbound atomically", err)
}
got, err := dest.GetAsset(a.ID)
if err != nil || got.Content != a.Content {
t.Fatal("restored asset mismatch", err)
}
}
func TestAssetImportPreviewHistoryAndRevisionGuard(t *testing.T) {
s := configStore(t)
a := backupAsset()
if err := s.RestoreBundle(Bundle{Version: 2, Assets: []model.Asset{a}}); err != nil {
t.Fatal(err)
}
a.Content = strings.ReplaceAll(a.Content, "asset-private-token", "replacement-private-token")
in := Bundle{Version: 2, Assets: []model.Asset{a}}
preview, err := s.PreviewBundle(in)
if err != nil || preview.Assets != 1 || len(preview.Resources) != 1 {
t.Fatal("asset preview missing", err)
}
raw, _ := json.Marshal(preview)
if bytes.Contains(raw, []byte("private-token")) || len(preview.Resources[0].Changes) != 1 || !preview.Resources[0].Changes[0].SecretChanged {
t.Fatal("asset preview leaked content or omitted secret change", string(raw))
}
if err := s.RestoreBundleChecked(in, preview.Revision); err != nil {
t.Fatal(err)
}
if err := s.RestoreBundleChecked(in, preview.Revision); !errors.Is(err, ErrConfigurationChanged) {
t.Fatal("asset update failed to invalidate preview", err)
}
diff, err := s.ConfigurationDiff("assets", a.ID, 1)
if err != nil || len(diff) != 1 || !diff[0].SecretChanged || diff[0].Before != nil || diff[0].After != nil {
t.Fatal("asset history content leaked", err)
}
if err := s.RestoreRevision("assets", a.ID, 1); err != nil {
t.Fatal(err)
}
got, err := s.GetAsset(a.ID)
if err != nil || got.Content != backupAsset().Content {
t.Fatal("asset history restore failed", err)
}
}
func TestAssetImportRejectsBrokenReferencesAtomically(t *testing.T) {
s := configStore(t)
a := backupAsset()
o := model.OutboundProfile{ID: "kube", Name: "kube", Outbound: model.Outbound{Mode: "k8s", Resource: "pod/db", KubeConfigAssetID: "missing"}}
in := Bundle{Version: 2, Assets: []model.Asset{a}, Outbounds: []model.OutboundProfile{o}}
if _, err := s.PreviewBundle(in); err == nil {
t.Fatal("preview accepted missing asset")
}
if err := s.RestoreBundle(in); err == nil {
t.Fatal("import accepted missing asset")
}
if _, err := s.GetAsset(a.ID); err == nil {
t.Fatal("invalid import partially committed")
}
in.Outbounds = nil
in.Assets = append(in.Assets, a)
if err := s.RestoreBundle(in); err == nil {
t.Fatal("import accepted duplicate assets")
}
}