mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 11:56:23 +08:00
fix(release):完善当前分发证明与质量门禁
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"bat-api/internal/backendrpc"
|
||||
)
|
||||
|
||||
func testAttestation(root, releaseID, integrity string, ready bool) *backendrpc.DistributionAttestation {
|
||||
return &backendrpc.DistributionAttestation{
|
||||
Available: true,
|
||||
Channel: "official",
|
||||
ReleaseID: releaseID,
|
||||
ResourceRoot: root,
|
||||
PublicationIdentity: "publication-" + releaseID,
|
||||
MappingIdentity: "mapping-" + releaseID,
|
||||
ManifestIdentity: "manifest-" + releaseID,
|
||||
EntryCount: 3,
|
||||
IntegrityStatus: integrity,
|
||||
Status: integrity,
|
||||
StatusCode: "distribution." + integrity,
|
||||
Ready: ready,
|
||||
VerificationGeneration: 4,
|
||||
}
|
||||
}
|
||||
|
||||
func testManifestPage(attestation *backendrpc.DistributionAttestation, offset int, entries int) *backendrpc.ResourceManifestPage {
|
||||
pageEntries := make([]backendrpc.ResourceManifestEntry, entries)
|
||||
for index := range pageEntries {
|
||||
size := uint64(index + 1)
|
||||
pageEntries[index] = backendrpc.ResourceManifestEntry{
|
||||
URL: "https://example.invalid/" + string(rune('a'+offset+index)),
|
||||
Destination: "resource-" + string(rune('a'+offset+index)),
|
||||
Bytes: &size,
|
||||
BLAKE3: "blake3",
|
||||
}
|
||||
}
|
||||
return &backendrpc.ResourceManifestPage{
|
||||
Available: true,
|
||||
Channel: "official",
|
||||
ReleaseID: attestation.ReleaseID,
|
||||
ResourceRoot: attestation.ResourceRoot,
|
||||
ManifestVersion: 1,
|
||||
PublicationIdentity: attestation.PublicationIdentity,
|
||||
MappingIdentity: attestation.MappingIdentity,
|
||||
ManifestIdentity: attestation.ManifestIdentity,
|
||||
Generation: attestation.VerificationGeneration,
|
||||
TotalEntries: attestation.EntryCount,
|
||||
Offset: offset,
|
||||
Limit: 2,
|
||||
Entries: pageEntries,
|
||||
}
|
||||
}
|
||||
|
||||
type pagedManifestBackend struct {
|
||||
*fakeBackend
|
||||
pages []*backendrpc.ResourceManifestPage
|
||||
params []backendrpc.ResourceManifestParams
|
||||
}
|
||||
|
||||
func (b *pagedManifestBackend) ResourceManifest(_ context.Context, params backendrpc.ResourceManifestParams) (*backendrpc.ResourceManifestPage, error) {
|
||||
b.params = append(b.params, params)
|
||||
pageIndex := len(b.params) - 1
|
||||
page := *b.pages[pageIndex]
|
||||
return &page, nil
|
||||
}
|
||||
|
||||
func TestFetchAllManifestEntriesRejectsMixedPages(t *testing.T) {
|
||||
attestation := testAttestation("/srv/official/current", "official-a", "verified", true)
|
||||
tests := []struct {
|
||||
name string
|
||||
mutate func(*backendrpc.ResourceManifestPage)
|
||||
}{
|
||||
{
|
||||
name: "release",
|
||||
mutate: func(page *backendrpc.ResourceManifestPage) { page.ReleaseID = "official-b" },
|
||||
},
|
||||
{
|
||||
name: "root",
|
||||
mutate: func(page *backendrpc.ResourceManifestPage) { page.ResourceRoot = "/srv/official/current-b" },
|
||||
},
|
||||
{
|
||||
name: "manifest identity",
|
||||
mutate: func(page *backendrpc.ResourceManifestPage) { page.ManifestIdentity = "manifest-b" },
|
||||
},
|
||||
{
|
||||
name: "total",
|
||||
mutate: func(page *backendrpc.ResourceManifestPage) { page.TotalEntries = 4 },
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
first := testManifestPage(attestation, 0, 2)
|
||||
second := testManifestPage(attestation, 2, 1)
|
||||
test.mutate(second)
|
||||
backend := &pagedManifestBackend{
|
||||
fakeBackend: &fakeBackend{},
|
||||
pages: []*backendrpc.ResourceManifestPage{first, second},
|
||||
}
|
||||
if _, _, _, err := fetchAllManifestEntriesWithPageSize(
|
||||
context.Background(),
|
||||
backend,
|
||||
attestation,
|
||||
2,
|
||||
); err == nil {
|
||||
t.Fatal("expected mixed-page validation error")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchAllManifestEntriesAcceptsMatchingGeneration(t *testing.T) {
|
||||
attestation := testAttestation("/srv/official/current", "official-a", "verified", true)
|
||||
backend := &pagedManifestBackend{
|
||||
fakeBackend: &fakeBackend{},
|
||||
pages: []*backendrpc.ResourceManifestPage{
|
||||
testManifestPage(attestation, 0, 2),
|
||||
testManifestPage(attestation, 2, 1),
|
||||
},
|
||||
}
|
||||
entries, version, root, err := fetchAllManifestEntriesWithPageSize(
|
||||
context.Background(),
|
||||
backend,
|
||||
attestation,
|
||||
2,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(entries) != 3 || version != 1 || root != attestation.ResourceRoot {
|
||||
t.Fatalf("entries=%d version=%d root=%q", len(entries), version, root)
|
||||
}
|
||||
if len(backend.params) != 2 ||
|
||||
backend.params[1].ReleaseID != attestation.ReleaseID ||
|
||||
backend.params[1].ExpectedManifestIdentity != attestation.ManifestIdentity {
|
||||
t.Fatalf("params=%+v", backend.params)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiscoverRejectsAttestationThenCatalogCurrentSwitch(t *testing.T) {
|
||||
root := fixtureRoot(t)
|
||||
backend := fixtureRPCBackend(t, root)
|
||||
backend.attestation = testAttestation(root, "official-a", "verified", true)
|
||||
catalog, err := json.Marshal(map[string]any{
|
||||
"available": true,
|
||||
"version": map[string]any{
|
||||
"id": "official-b",
|
||||
"resource_root": root,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
backend.catalog = catalog
|
||||
result, err := DiscoverAndIndex(context.Background(), backend, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if result.Index == nil || result.Index.Summary().Ready || result.Index.Summary().EntryCount != 0 {
|
||||
t.Fatalf("summary=%+v", result.Index.Summary())
|
||||
}
|
||||
if len(backend.manifestParams) != 0 {
|
||||
t.Fatalf("manifest should not be fetched after current switch: %+v", backend.manifestParams)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHealthyAttestationThenCurrentSwitchClearsSnapshot(t *testing.T) {
|
||||
root := copyFixtureRoot(t)
|
||||
backend := fixtureRPCBackend(t, root)
|
||||
backend.attestation = testAttestation(root, "official-a", "verified", true)
|
||||
backend.attestation.EntryCount = 2
|
||||
backend.catalog = mustCatalogForTest(t, root, "official-a")
|
||||
backend.manifest.ReleaseID = "official-a"
|
||||
backend.manifest.PublicationIdentity = "publication-official-a"
|
||||
backend.manifest.MappingIdentity = "mapping-official-a"
|
||||
backend.manifest.ManifestIdentity = "manifest-official-a"
|
||||
backend.manifest.Generation = 4
|
||||
cfg := DefaultConfig()
|
||||
cfg.RefreshInterval = 0
|
||||
if err := cfg.Normalize(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
server := NewServer(cfg, backend, nil)
|
||||
if err := server.Refresh(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !server.index().Summary().Ready {
|
||||
t.Fatal("initial snapshot should be ready")
|
||||
}
|
||||
|
||||
backend.attestation = testAttestation(root, "official-b", "verified", true)
|
||||
backend.catalog = mustCatalogForTest(t, root, "official-b")
|
||||
if err := server.Refresh(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if summary := server.index().Summary(); summary.Ready || summary.ResourceRoot != "" {
|
||||
t.Fatalf("mixed snapshot was retained: %+v", summary)
|
||||
}
|
||||
}
|
||||
|
||||
func mustCatalogForTest(t *testing.T, root, releaseID string) json.RawMessage {
|
||||
t.Helper()
|
||||
raw, err := json.Marshal(map[string]any{
|
||||
"available": true,
|
||||
"version": map[string]any{
|
||||
"id": releaseID,
|
||||
"resource_root": root,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
func TestStaleOrInvalidMatchingAttestationNeverReadiesIndex(t *testing.T) {
|
||||
for _, integrity := range []string{"stale", "invalid"} {
|
||||
t.Run(integrity, func(t *testing.T) {
|
||||
root := fixtureRoot(t)
|
||||
backend := fixtureRPCBackend(t, root)
|
||||
backend.attestation = testAttestation(root, "official-a", integrity, false)
|
||||
backend.attestation.EntryCount = 2
|
||||
backend.catalog = mustCatalogForTest(t, root, "official-a")
|
||||
backend.manifest.ReleaseID = "official-a"
|
||||
backend.manifest.PublicationIdentity = "publication-official-a"
|
||||
backend.manifest.MappingIdentity = "mapping-official-a"
|
||||
backend.manifest.ManifestIdentity = "manifest-official-a"
|
||||
backend.manifest.Generation = 4
|
||||
result, err := DiscoverAndIndex(context.Background(), backend, "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if result.Index == nil || result.Index.Summary().Ready ||
|
||||
result.Index.Summary().Distribution.Ready {
|
||||
t.Fatalf("summary=%+v", result.Index.Summary())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user