mirror of
https://github.com/Yuyi-Oak/BlueArchiveToolkit.git
synced 2026-09-18 07:24:55 +08:00
262 lines
9.7 KiB
Go
262 lines
9.7 KiB
Go
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"bat-api/internal/backendrpc"
|
|
)
|
|
|
|
func readContractFixture(t *testing.T, name string) []byte {
|
|
t.Helper()
|
|
data, err := os.ReadFile(filepath.Join("testdata", "contract", name))
|
|
if err != nil {
|
|
t.Fatalf("read contract fixture %s: %v", name, err)
|
|
}
|
|
return data
|
|
}
|
|
|
|
func TestRustContractFixturesPreserveGoMirror(t *testing.T) {
|
|
availableRaw := readContractFixture(t, "catalog-status.available.json")
|
|
unavailableRaw := readContractFixture(t, "catalog-status.unavailable.json")
|
|
manifestRaw := readContractFixture(t, "resource-manifest.page0.json")
|
|
attestationRaw := readContractFixture(t, "release-attestation.json")
|
|
snapshotRaw := readContractFixture(t, "official-sync-snapshot.json")
|
|
|
|
for name, raw := range map[string][]byte{
|
|
"catalog available": availableRaw,
|
|
"catalog unavailable": unavailableRaw,
|
|
"resource manifest": manifestRaw,
|
|
"attestation": attestationRaw,
|
|
"snapshot": snapshotRaw,
|
|
} {
|
|
if bytes.Contains(raw, []byte("/tmp/")) {
|
|
t.Fatalf("%s contains an absolute temporary path", name)
|
|
}
|
|
if bytes.Contains(raw, []byte("r93_fixture")) {
|
|
t.Fatalf("%s contains a concrete release token", name)
|
|
}
|
|
}
|
|
|
|
summary, resourceRoot, available := parseCatalogStatus(availableRaw)
|
|
if !available || summary == nil {
|
|
t.Fatal("available catalog fixture was not accepted")
|
|
}
|
|
if summary.Status != "published" || summary.StatusCode != "official.published" {
|
|
t.Fatalf("catalog status=%+v", summary)
|
|
}
|
|
if summary.DistributionStatusCode != "distribution.ready" {
|
|
t.Fatalf("distribution status=%q", summary.DistributionStatusCode)
|
|
}
|
|
if resourceRoot != "${RESOURCE_ROOT}" || summary.VersionID != "${VERSION_ID}" {
|
|
t.Fatalf("catalog root/version=%q/%q", resourceRoot, summary.VersionID)
|
|
}
|
|
if summary.LauncherMetadata == nil || summary.GameMainConfig == nil {
|
|
t.Fatalf("catalog optional objects missing: %+v", summary)
|
|
}
|
|
if summary.LauncherMetadata.GameLowestVersion != "" {
|
|
t.Fatalf("null optional field became non-empty: %q", summary.LauncherMetadata.GameLowestVersion)
|
|
}
|
|
if summary.GameMainConfig.DefaultConnectionGroup != "${CONNECTION_GROUP}" {
|
|
t.Fatalf("game config=%+v", summary.GameMainConfig)
|
|
}
|
|
|
|
if got, _, ok := parseCatalogStatus(unavailableRaw); got != nil || ok {
|
|
t.Fatalf("unavailable catalog parsed as available: got=%+v ok=%v", got, ok)
|
|
}
|
|
|
|
var manifest backendrpc.ResourceManifestPage
|
|
if err := json.Unmarshal(manifestRaw, &manifest); err != nil {
|
|
t.Fatalf("decode resource manifest: %v", err)
|
|
}
|
|
if !manifest.Available || manifest.Channel != "official" ||
|
|
manifest.ManifestVersion != 1 || manifest.TotalEntries != 2 ||
|
|
manifest.ReleaseID != "${VERSION_ID}" ||
|
|
manifest.PublicationIdentity != "${PUBLICATION_IDENTITY}" ||
|
|
manifest.ManifestIdentity != "${MANIFEST_IDENTITY}" ||
|
|
manifest.Generation != 7 {
|
|
t.Fatalf("manifest header=%+v", manifest)
|
|
}
|
|
if len(manifest.Entries) != 2 {
|
|
t.Fatalf("manifest entries=%d", len(manifest.Entries))
|
|
}
|
|
if manifest.Entries[0].Bytes == nil || *manifest.Entries[0].Bytes != 21 {
|
|
t.Fatalf("manifest first bytes=%v", manifest.Entries[0].Bytes)
|
|
}
|
|
if manifest.Entries[0].BLAKE3 == "" || !strings.Contains(manifest.Entries[0].Destination, "{addressables-root}") {
|
|
t.Fatalf("manifest first entry=%+v", manifest.Entries[0])
|
|
}
|
|
|
|
var attestation backendrpc.DistributionAttestation
|
|
if err := json.Unmarshal(attestationRaw, &attestation); err != nil {
|
|
t.Fatalf("decode attestation: %v", err)
|
|
}
|
|
if !attestation.Available || !attestation.Ready ||
|
|
attestation.ReleaseID != "${VERSION_ID}" ||
|
|
attestation.ManifestIdentity != "${MANIFEST_IDENTITY}" ||
|
|
attestation.VerificationGeneration != 7 ||
|
|
attestation.MaxAgeSeconds != 7260 ||
|
|
attestation.VerifiedAt == nil || *attestation.VerifiedAt != 1000 {
|
|
t.Fatalf("attestation=%+v", attestation)
|
|
}
|
|
|
|
var snapshot struct {
|
|
AppVersion string `json:"app_version"`
|
|
BundleVersion string `json:"bundle_version"`
|
|
GameMainConfigBootstrap *GameMainConfigSummary `json:"game_main_config_bootstrap"`
|
|
LauncherMetadata *LauncherMetadataSummary `json:"launcher_metadata"`
|
|
LegacyGameMainConfig json.RawMessage `json:"game_main_config"`
|
|
}
|
|
if err := json.Unmarshal(snapshotRaw, &snapshot); err != nil {
|
|
t.Fatalf("decode snapshot: %v", err)
|
|
}
|
|
if snapshot.AppVersion != "${APP_VERSION}" || snapshot.BundleVersion != "${BUNDLE_VERSION}" {
|
|
t.Fatalf("snapshot versions=%q/%q", snapshot.AppVersion, snapshot.BundleVersion)
|
|
}
|
|
if snapshot.GameMainConfigBootstrap == nil || snapshot.GameMainConfigBootstrap.ServerInfoDataURL == "" {
|
|
t.Fatalf("snapshot game config=%+v", snapshot.GameMainConfigBootstrap)
|
|
}
|
|
if snapshot.GameMainConfigBootstrap.ServerInfoDataURL != "https://yostar-serverinfo.bluearchiveyostar.com/{server-info-path}" {
|
|
t.Fatalf("snapshot server info=%q", snapshot.GameMainConfigBootstrap.ServerInfoDataURL)
|
|
}
|
|
if snapshot.LauncherMetadata == nil || snapshot.LauncherMetadata.ManifestSource == "" {
|
|
t.Fatalf("snapshot launcher metadata=%+v", snapshot.LauncherMetadata)
|
|
}
|
|
if len(snapshot.LegacyGameMainConfig) != 0 {
|
|
t.Fatalf("legacy game_main_config field unexpectedly present: %s", snapshot.LegacyGameMainConfig)
|
|
}
|
|
}
|
|
|
|
func TestTranslationMemoryRustContractMirror(t *testing.T) {
|
|
raw := []byte(`{
|
|
"available": true,
|
|
"path": "${TM_PATH}",
|
|
"source_text": "${SOURCE_TEXT}",
|
|
"source_context": {
|
|
"destination": "${DESTINATION}",
|
|
"field_path": "${FIELD_PATH}"
|
|
},
|
|
"matches": [{
|
|
"entry": {
|
|
"record_id": "${RECORD_ID}",
|
|
"source_text": "${SOURCE_TEXT}",
|
|
"source_hash": "${SOURCE_HASH}",
|
|
"normalized_source_text": "${NORMALIZED_SOURCE_TEXT}",
|
|
"source_context": {
|
|
"destination": "${DESTINATION}",
|
|
"field_path": "${FIELD_PATH}"
|
|
},
|
|
"source_context_hash": "${SOURCE_CONTEXT_HASH}",
|
|
"translated_text": "${TRANSLATED_TEXT}",
|
|
"translation_source_kind": "provider",
|
|
"trust_status": "trusted",
|
|
"official_release_id": "${OFFICIAL_RELEASE_ID}",
|
|
"source_trace": {
|
|
"official_release_id": "${OFFICIAL_RELEASE_ID}",
|
|
"unit_id": "${UNIT_ID}",
|
|
"task_id": "${TASK_ID}",
|
|
"destination": "${DESTINATION}",
|
|
"archive_entry": "${ARCHIVE_ENTRY}",
|
|
"serialized_file": "${SERIALIZED_FILE}",
|
|
"path_id": 42,
|
|
"class_id": 114,
|
|
"field_path": "${FIELD_PATH}",
|
|
"format": "json",
|
|
"asset_name": "${ASSET_NAME}",
|
|
"text_source_kind": "text_asset"
|
|
},
|
|
"provider": "${PROVIDER}",
|
|
"provider_run_id": "${PROVIDER_RUN_ID}",
|
|
"created_unix_seconds": 100,
|
|
"updated_unix_seconds": 200,
|
|
"trusted_unix_seconds": 200,
|
|
"trusted_by": "${REVIEWER}",
|
|
"trusted_reason": "${TRUST_REASON}"
|
|
},
|
|
"match_kind": "strong_exact",
|
|
"can_auto_reuse": true
|
|
}]
|
|
}`)
|
|
if bytes.Contains(raw, []byte("/tmp/")) {
|
|
t.Fatal("TM mirror contains a local temporary path")
|
|
}
|
|
|
|
var report backendrpc.TranslationMemoryQueryReport
|
|
if err := json.Unmarshal(raw, &report); err != nil {
|
|
t.Fatalf("decode TM query mirror: %v", err)
|
|
}
|
|
if !report.Available || report.Path != "${TM_PATH}" ||
|
|
report.SourceText != "${SOURCE_TEXT}" ||
|
|
report.SourceContext["field_path"] != "${FIELD_PATH}" ||
|
|
len(report.Matches) != 1 {
|
|
t.Fatalf("TM report=%+v", report)
|
|
}
|
|
match := report.Matches[0]
|
|
if match.MatchKind != "strong_exact" || !match.CanAutoReuse ||
|
|
match.Entry.TrustStatus != "trusted" ||
|
|
match.Entry.TranslationSourceKind != "provider" ||
|
|
match.Entry.SourceTrace.PathID == nil || *match.Entry.SourceTrace.PathID != 42 ||
|
|
match.Entry.SourceTrace.ClassID == nil || *match.Entry.SourceTrace.ClassID != 114 ||
|
|
match.Entry.TrustedBy == nil || *match.Entry.TrustedBy != "${REVIEWER}" {
|
|
t.Fatalf("TM match=%+v", match)
|
|
}
|
|
|
|
var missing backendrpc.TranslationMemorySummaryReport
|
|
if err := json.Unmarshal([]byte(`{
|
|
"available": false,
|
|
"path": "${TM_PATH}",
|
|
"reason": "database_missing"
|
|
}`), &missing); err != nil {
|
|
t.Fatalf("decode missing TM summary mirror: %v", err)
|
|
}
|
|
if missing.Available || missing.Summary != nil || missing.SchemaVersion != nil ||
|
|
missing.Reason != "database_missing" {
|
|
t.Fatalf("missing TM summary=%+v", missing)
|
|
}
|
|
}
|
|
|
|
func TestGlossaryRustContractMirror(t *testing.T) {
|
|
raw := readContractFixture(t, "glossary-query.json")
|
|
if bytes.Contains(raw, []byte("/tmp/")) {
|
|
t.Fatal("Glossary mirror contains a local temporary path")
|
|
}
|
|
var report backendrpc.GlossaryQueryReport
|
|
if err := json.Unmarshal(raw, &report); err != nil {
|
|
t.Fatalf("decode Glossary query mirror: %v", err)
|
|
}
|
|
if !report.Available || report.Path != "${GLOSSARY_PATH}" ||
|
|
report.SourceText != "${SOURCE_TEXT}" || len(report.Terms) != 1 {
|
|
t.Fatalf("Glossary report=%+v", report)
|
|
}
|
|
term := report.Terms[0]
|
|
if term.TermID != "${TERM_ID}" ||
|
|
term.SourceTerm != "${SOURCE_TERM}" ||
|
|
term.RecommendedTranslation != "${RECOMMENDED_TRANSLATION}" ||
|
|
term.ReviewStatus != backendrpc.GlossaryStatusApproved ||
|
|
term.Source.SourceKind != "manual" ||
|
|
term.Source.ObservedUnixSeconds != 100 ||
|
|
len(term.History) != 2 ||
|
|
term.History[0].ReviewStatus != backendrpc.GlossaryStatusDraft ||
|
|
term.History[1].Action != "approved" ||
|
|
term.History[1].ReviewStatus != backendrpc.GlossaryStatusApproved ||
|
|
term.History[1].Snapshot.Priority != 10 {
|
|
t.Fatalf("Glossary term=%+v", term)
|
|
}
|
|
var missing backendrpc.GlossarySummaryReport
|
|
if err := json.Unmarshal([]byte(`{
|
|
"available": false,
|
|
"path": "${GLOSSARY_PATH}",
|
|
"reason": "database_missing"
|
|
}`), &missing); err != nil {
|
|
t.Fatalf("decode missing Glossary summary mirror: %v", err)
|
|
}
|
|
if missing.Available || missing.Summary != nil || missing.SchemaVersion != nil ||
|
|
missing.Reason != "database_missing" {
|
|
t.Fatalf("missing Glossary summary=%+v", missing)
|
|
}
|
|
}
|