4cf3471398
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
192 lines
4.1 KiB
Go
192 lines
4.1 KiB
Go
package steamvdf
|
|
|
|
import "testing"
|
|
|
|
// Realistic ACF as steamcmd writes it for a 7DTD dedicated server pinned
|
|
// to a frozen beta branch.
|
|
const acf7dtd = `"AppState"
|
|
{
|
|
"appid" "294420"
|
|
"Universe" "1"
|
|
"name" "7 Days to Die Dedicated Server"
|
|
"StateFlags" "4"
|
|
"installdir" "7 Days to Die Dedicated Server"
|
|
"LastUpdated" "1750912345"
|
|
"SizeOnDisk" "14680064000"
|
|
"StagingSize" "0"
|
|
"buildid" "14503775"
|
|
"LastOwner" "76561190000000000"
|
|
"UpdateResult" "0"
|
|
"BytesToDownload" "0"
|
|
"BytesDownloaded" "0"
|
|
"AutoUpdateBehavior" "0"
|
|
"AllowOtherDownloadsWhileRunning" "0"
|
|
"ScheduledAutoUpdate" "0"
|
|
"InstalledDepots"
|
|
{
|
|
"294422"
|
|
{
|
|
"manifest" "1118115119020227469"
|
|
"size" "14680064000"
|
|
}
|
|
}
|
|
"UserConfig"
|
|
{
|
|
"betakey" "v2.6"
|
|
}
|
|
"MountedConfig"
|
|
{
|
|
"betakey" "v2.6"
|
|
}
|
|
}
|
|
`
|
|
|
|
const acfPublic = `"AppState"
|
|
{
|
|
"appid" "2430930"
|
|
"name" "Palworld Dedicated Server"
|
|
"buildid" "20112233"
|
|
"UserConfig"
|
|
{
|
|
}
|
|
}
|
|
`
|
|
|
|
func TestParseAppManifestBetaBranch(t *testing.T) {
|
|
m, err := ParseAppManifest(acf7dtd)
|
|
if err != nil {
|
|
t.Fatalf("ParseAppManifest: %v", err)
|
|
}
|
|
if m.AppID != "294420" || m.BuildID != "14503775" || m.BetaKey != "v2.6" {
|
|
t.Fatalf("got %+v", m)
|
|
}
|
|
if m.Name != "7 Days to Die Dedicated Server" {
|
|
t.Fatalf("name: %q", m.Name)
|
|
}
|
|
}
|
|
|
|
func TestParseAppManifestPublic(t *testing.T) {
|
|
m, err := ParseAppManifest(acfPublic)
|
|
if err != nil {
|
|
t.Fatalf("ParseAppManifest: %v", err)
|
|
}
|
|
if m.BuildID != "20112233" || m.BetaKey != "" {
|
|
t.Fatalf("got %+v", m)
|
|
}
|
|
}
|
|
|
|
func TestParseAppManifestNoBuildID(t *testing.T) {
|
|
if _, err := ParseAppManifest(`"AppState" { "appid" "1" }`); err == nil {
|
|
t.Fatal("expected error for manifest without buildid")
|
|
}
|
|
}
|
|
|
|
// app_info_print output as steamcmd emits it: banner noise, the numeric
|
|
// appid root key, escaped quotes, nested branches with pwdrequired etc.
|
|
const appInfo = `Redirecting stderr to '/root/Steam/logs/stderr.txt'
|
|
Loading Steam API...OK
|
|
Connecting anonymously to Steam Public...OK
|
|
Waiting for client config...OK
|
|
Waiting for user info...OK
|
|
AppID : 294420, change number : 27581234/0, last change : Sat Jun 28 12:00:00 2026
|
|
"294420"
|
|
{
|
|
"common"
|
|
{
|
|
"name" "7 Days to Die Dedicated Server"
|
|
"type" "Tool"
|
|
}
|
|
"config"
|
|
{
|
|
"installdir" "7 Days to Die Dedicated Server"
|
|
}
|
|
"depots"
|
|
{
|
|
"294422"
|
|
{
|
|
"name" "Linux Depot"
|
|
"config"
|
|
{
|
|
"oslist" "linux"
|
|
}
|
|
}
|
|
"branches"
|
|
{
|
|
"public"
|
|
{
|
|
"buildid" "19216811"
|
|
"timeupdated" "1751168000"
|
|
}
|
|
"latest_experimental"
|
|
{
|
|
"buildid" "19333444"
|
|
"description" "Latest \"experimental\" build"
|
|
"timeupdated" "1752168000"
|
|
}
|
|
"v2.6"
|
|
{
|
|
"buildid" "14503775"
|
|
"description" "2.6 Stable"
|
|
"timeupdated" "1750168000"
|
|
}
|
|
"private_qa"
|
|
{
|
|
"buildid" "19999999"
|
|
"pwdrequired" "1"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`
|
|
|
|
func TestParseAppInfoBranches(t *testing.T) {
|
|
b, err := ParseAppInfoBranches(appInfo, "294420")
|
|
if err != nil {
|
|
t.Fatalf("ParseAppInfoBranches: %v", err)
|
|
}
|
|
want := map[string]string{
|
|
"public": "19216811",
|
|
"latest_experimental": "19333444",
|
|
"v2.6": "14503775",
|
|
"private_qa": "19999999",
|
|
}
|
|
for k, v := range want {
|
|
if b[k] != v {
|
|
t.Fatalf("branch %s: got %q want %q (all: %v)", k, b[k], v, b)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseAppInfoBranchesUnknownRootKey(t *testing.T) {
|
|
// Caller passes the wrong appid; fallback finds the object anyway.
|
|
b, err := ParseAppInfoBranches(appInfo, "999999")
|
|
if err != nil {
|
|
t.Fatalf("fallback failed: %v", err)
|
|
}
|
|
if b["public"] != "19216811" {
|
|
t.Fatalf("got %v", b)
|
|
}
|
|
}
|
|
|
|
func TestParseAppInfoNoBranches(t *testing.T) {
|
|
if _, err := ParseAppInfoBranches(`"1" { "common" { "name" "x" } }`, "1"); err == nil {
|
|
t.Fatal("expected error when branches missing")
|
|
}
|
|
}
|
|
|
|
func TestParseToleratesCommentsAndUnquoted(t *testing.T) {
|
|
kv, err := Parse("// comment\nAppState { key value // trailing\n nested { a b } }")
|
|
if err != nil {
|
|
t.Fatalf("Parse: %v", err)
|
|
}
|
|
if kv.Get("AppState", "key") != "value" || kv.Get("appstate", "nested", "a") != "b" {
|
|
t.Fatalf("got %v", kv)
|
|
}
|
|
}
|
|
|
|
func TestParseUnterminated(t *testing.T) {
|
|
if _, err := Parse(`"A" { "k" "v"`); err == nil {
|
|
t.Fatal("expected unterminated-object error")
|
|
}
|
|
}
|