Files
wehub-resource-sync f99010fae1
Desktop Artifacts / Desktop Build (Linux) (push) Waiting to run
Desktop Artifacts / Desktop Build (Windows) (push) Waiting to run
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Waiting to run
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Waiting to run
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

389 lines
14 KiB
Go

package parser
import (
"database/sql"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// agyBaselineCreateStatements are the exact CREATE statements of the verified
// agy 1.0.7-1.0.10 schema (user_version=1, 7 tables, 2 indices), copied
// byte-for-byte from a real ~/.gemini/antigravity-cli database. Executing them
// verbatim makes sqlite_master store identical sql text, so the Go fingerprint
// reproduces agy-reader's recorded sha256:1ca98426...f72f43.
var agyBaselineCreateStatements = []string{
"CREATE TABLE `trajectory_meta` (`trajectory_id` text,`cascade_id` text," +
"`trajectory_type` integer,`source` integer,PRIMARY KEY (`trajectory_id`))",
"CREATE TABLE `steps` (`idx` integer,`step_type` integer NOT NULL DEFAULT 0," +
"`status` integer NOT NULL DEFAULT 0," +
"`has_subtrajectory` numeric NOT NULL DEFAULT false," +
"`metadata` blob,`error_details` blob,`permissions` blob," +
"`task_details` blob,`render_info` blob,`step_payload` blob," +
"`step_format` integer NOT NULL DEFAULT 0,PRIMARY KEY (`idx`))",
"CREATE TABLE `gen_metadata` (`idx` integer,`data` blob," +
"`size` integer NOT NULL DEFAULT 0,PRIMARY KEY (`idx`))",
"CREATE TABLE `executor_metadata` (`idx` integer,`data` blob,PRIMARY KEY (`idx`))",
"CREATE TABLE `parent_references` (`idx` integer,`data` blob,PRIMARY KEY (`idx`))",
"CREATE TABLE `trajectory_metadata_blob` (`id` text DEFAULT \"main\"," +
"`data` blob,PRIMARY KEY (`id`))",
"CREATE TABLE `battle_mode_infos` (`idx` integer,`data` blob,PRIMARY KEY (`idx`))",
"CREATE INDEX `idx_steps_status` ON `steps`(`status`)",
"CREATE INDEX `idx_steps_step_type` ON `steps`(`step_type`)",
}
// createAntigravityBaselineSchemaDB writes a .db whose schema fingerprint
// matches the verified agy 1.0.7-1.0.10 baseline. It executes the real CREATE
// statements verbatim and sets user_version=1, then seeds two decodable steps
// so the parser produces a usable session.
func createAntigravityBaselineSchemaDB(t *testing.T, path string) {
t.Helper()
db, err := sql.Open("sqlite3", path)
require.NoError(t, err, "open")
defer db.Close()
for _, stmt := range agyBaselineCreateStatements {
mustExec(t, db, stmt)
}
mustExec(t, db, "PRAGMA user_version = 1")
tsEarly := encodePB([]pbField{
{num: 1, wire: pbWireVarint, varint: 1779000000},
})
userPayload := encodePB([]pbField{
{num: 5, wire: pbWireBytes, bytes: tsEarly},
{num: 17, wire: pbWireBytes, bytes: []byte("user prompt text goes here")},
})
tsLate := encodePB([]pbField{
{num: 1, wire: pbWireVarint, varint: 1779000100},
})
asstPayload := encodePB([]pbField{
{num: 5, wire: pbWireBytes, bytes: tsLate},
{num: 17, wire: pbWireBytes, bytes: []byte("assistant reply content body")},
})
mustExec(t, db,
"INSERT INTO steps (idx, step_type, step_payload) VALUES (?, ?, ?)",
0, 14, userPayload)
mustExec(t, db,
"INSERT INTO steps (idx, step_type, step_payload) VALUES (?, ?, ?)",
1, 17, asstPayload)
}
// openSchemaDB opens a fresh SQLite db at path and returns a handle the caller
// must close. The db is created by running each statement in stmts plus
// user_version.
func openSchemaDB(
t *testing.T, path string, userVersion int, stmts ...string,
) *sql.DB {
t.Helper()
build, err := sql.Open("sqlite3", path)
require.NoError(t, err, "open for build")
for _, stmt := range stmts {
_, err := build.Exec(stmt)
require.NoError(t, err, "exec %q", stmt)
}
_, err = build.Exec("PRAGMA user_version = " + itoa(userVersion))
require.NoError(t, err, "set user_version")
require.NoError(t, build.Close(), "close build")
db, err := sql.Open("sqlite3", path)
require.NoError(t, err, "reopen")
return db
}
const agyBaselineFullFingerprint = "1ca98426f561fe73223c8620a238405030fdb3014444d970e1300a6009f72f43"
func TestAntigravitySchemaLabel(t *testing.T) {
tests := []struct {
name string
fingerprint string
want string
}{
{
name: "known baseline maps to release range",
fingerprint: agyBaselineFullFingerprint,
want: "1.0.7-1.0.10",
},
{
name: "unknown fingerprint becomes agy-schema marker",
fingerprint: "deadbeefcafe0000111122223333444455556666777788889999aaaabbbbcccc",
want: "agy-schema:deadbeefcafe",
},
{
name: "empty fingerprint yields empty label",
fingerprint: "",
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := antigravitySchemaLabel(tt.fingerprint)
assert.Equal(t, tt.want, got)
if got != "" {
assert.LessOrEqual(t, len(got), 63,
"label must stay under 64 chars")
}
})
}
}
// TestDecodeConfidence exercises the single-source-of-truth confidence rule
// derived from an agent name and its source_version label. It covers the
// agent gate against SourceVersion being a generic field set by other parsers.
func TestDecodeConfidence(t *testing.T) {
tests := []struct {
name string
agent string
sourceVersion string
want string
}{
{
name: "antigravity known range is high",
agent: string(AgentAntigravity),
sourceVersion: "1.0.7-1.0.10",
want: DecodeConfidenceHigh,
},
{
name: "antigravity-cli known range is high",
agent: string(AgentAntigravityCLI),
sourceVersion: "1.0.7-1.0.10",
want: DecodeConfidenceHigh,
},
{
name: "antigravity unknown schema is low",
agent: string(AgentAntigravity),
sourceVersion: antigravitySchemaUnknownPrefix + "abc123def456",
want: DecodeConfidenceLow,
},
{
name: "antigravity-cli unknown schema is low",
agent: string(AgentAntigravityCLI),
sourceVersion: antigravitySchemaUnknownPrefix + "abc123def456",
want: DecodeConfidenceLow,
},
{
name: "antigravity empty source_version yields no value",
agent: string(AgentAntigravity),
sourceVersion: "",
want: "",
},
{
name: "antigravity-cli empty source_version yields no value",
agent: string(AgentAntigravityCLI),
sourceVersion: "",
want: "",
},
{
name: "piebald generic label yields no value",
agent: string(AgentPiebald),
sourceVersion: "piebald-appdb-v1",
want: "",
},
{
name: "commandcode generic label yields no value",
agent: string(AgentCommandCode),
sourceVersion: "2",
want: "",
},
{
name: "other agent with agy-schema-shaped label yields no value",
agent: string(AgentClaude),
sourceVersion: antigravitySchemaUnknownPrefix + "abc123def456",
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want,
DecodeConfidence(tt.agent, tt.sourceVersion))
})
}
}
// TestAntigravitySchemaFingerprintBaseline verifies the Go fingerprint of the
// real baseline schema matches agy-reader's recorded sha256, so the
// known-label mapping actually fires on real databases.
func TestAntigravitySchemaFingerprintBaseline(t *testing.T) {
dir := t.TempDir()
stmts := append([]string{}, agyBaselineCreateStatements...)
db := openSchemaDB(t, filepath.Join(dir, "baseline.db"), 1, stmts...)
defer db.Close()
fp, err := antigravitySchemaFingerprint(db)
require.NoError(t, err)
assert.Equal(t, agyBaselineFullFingerprint, fp,
"Go fingerprint must equal agy-reader recorded sha256")
assert.Equal(t, "1.0.7-1.0.10", antigravitySchemaLabel(fp))
}
func TestAntigravitySchemaFingerprintCases(t *testing.T) {
dir := t.TempDir()
tests := []struct {
name string
userVersion int
stmts []string
wantLabel string
}{
{
name: "baseline schema known label",
userVersion: 1,
stmts: agyBaselineCreateStatements,
wantLabel: "1.0.7-1.0.10",
},
{
name: "extra table yields unknown marker",
userVersion: 1,
stmts: append(append([]string{}, agyBaselineCreateStatements...),
"CREATE TABLE `future_widget` (`idx` integer,`data` blob,"+
"PRIMARY KEY (`idx`))"),
wantLabel: "", // checked as marker prefix below
},
{
name: "bumped user_version yields unknown marker",
userVersion: 2,
stmts: agyBaselineCreateStatements,
wantLabel: "",
},
}
for i, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
path := filepath.Join(dir, itoa(i)+".db")
db := openSchemaDB(t, path, tt.userVersion, tt.stmts...)
defer db.Close()
fp, err := antigravitySchemaFingerprint(db)
require.NoError(t, err)
label := antigravitySchemaLabel(fp)
if tt.wantLabel != "" {
assert.Equal(t, tt.wantLabel, label)
} else {
assert.True(t,
strings.HasPrefix(label, antigravitySchemaUnknownPrefix),
"mutated schema should produce unknown marker, got %q",
label)
assert.NotEqual(t, agyBaselineFullFingerprint, fp,
"mutated schema must not reuse baseline fingerprint")
}
})
}
}
// TestAntigravitySchemaFingerprintDeterministic asserts the same schema always
// produces the same fingerprint across independent databases.
func TestAntigravitySchemaFingerprintDeterministic(t *testing.T) {
dir := t.TempDir()
stmts := append([]string{}, agyBaselineCreateStatements...)
db1 := openSchemaDB(t, filepath.Join(dir, "a.db"), 1, stmts...)
defer db1.Close()
db2 := openSchemaDB(t, filepath.Join(dir, "b.db"), 1, stmts...)
defer db2.Close()
fp1, err := antigravitySchemaFingerprint(db1)
require.NoError(t, err)
fp2, err := antigravitySchemaFingerprint(db2)
require.NoError(t, err)
assert.Equal(t, fp1, fp2, "identical schema must hash identically")
}
// TestAntigravityIDESourceVersionBaseline verifies the IDE parser stamps the
// schema-fingerprint label when the .db matches the baseline schema.
func TestAntigravityIDESourceVersionBaseline(t *testing.T) {
root := t.TempDir()
id := "aaaaaaaa-1111-2222-3333-444444444444"
mustMkdir(t, filepath.Join(root, "conversations"))
dbPath := filepath.Join(root, "conversations", id+".db")
createAntigravityBaselineSchemaDB(t, dbPath)
sess, _, _, err := parseAntigravityTestSession(t, dbPath, "/tmp/proj", "m")
require.NoError(t, err)
require.NotNil(t, sess)
assert.Equal(t, "1.0.7-1.0.10", sess.SourceVersion)
}
// TestAntigravityCLISourceVersionBaseline verifies the CLI .db parse path
// stamps the same label as the IDE path for the baseline schema.
func TestAntigravityCLISourceVersionBaseline(t *testing.T) {
root := t.TempDir()
id := "bbbbbbbb-1111-2222-3333-444444444444"
mustMkdir(t, filepath.Join(root, "conversations"))
dbPath := filepath.Join(root, "conversations", id+".db")
createAntigravityBaselineSchemaDB(t, dbPath)
sess, _, err := parseAntigravityCLITestSession(t, dbPath, "/tmp/proj", "m")
require.NoError(t, err)
require.NotNil(t, sess)
assert.Equal(t, "1.0.7-1.0.10", sess.SourceVersion)
}
// TestAntigravityIDESourceVersionUnknownSchema verifies that a .db with a
// non-baseline schema (the minimal 2-table test schema, no user_version)
// gets an agy-schema:<hex> marker rather than a fabricated release.
func TestAntigravityIDESourceVersionUnknownSchema(t *testing.T) {
root := t.TempDir()
id := "cccccccc-1111-2222-3333-444444444444"
mustMkdir(t, filepath.Join(root, "conversations"))
dbPath := filepath.Join(root, "conversations", id+".db")
createAntigravityTestDB(t, dbPath)
sess, _, _, err := parseAntigravityTestSession(t, dbPath, "/tmp/proj", "m")
require.NoError(t, err)
require.NotNil(t, sess)
assert.True(t,
strings.HasPrefix(sess.SourceVersion, antigravitySchemaUnknownPrefix),
"non-baseline schema should yield unknown marker, got %q",
sess.SourceVersion)
}
// TestAntigravityCLIDBStepsCarriesSourceVersionOnStepError verifies that a CLI
// .db whose schema is readable but whose steps query fails still returns the
// schema-fingerprint label. Without this the parser falls back to the
// trajectory sidecar and persists a session with an empty SourceVersion,
// silently losing the agy-schema marker for an unrecognized (newer) schema.
func TestAntigravityCLIDBStepsCarriesSourceVersionOnStepError(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "nosteps.db")
// A readable schema with NO steps table: the fingerprint query over
// sqlite_master succeeds, but loadAntigravityStepsWithRawCount's
// `SELECT ... FROM steps` fails. The lone table also makes the schema
// non-baseline, so the expected label is an agy-schema:<prefix> marker.
db := openSchemaDB(t, path, 1,
"CREATE TABLE `trajectory_meta` (`trajectory_id` text,"+
"PRIMARY KEY (`trajectory_id`))")
require.NoError(t, db.Close())
result, err := loadAntigravityCLIDBSteps(path)
require.Error(t, err, "missing steps table must fail the step query")
assert.True(t,
strings.HasPrefix(result.sourceVersion, antigravitySchemaUnknownPrefix),
"readable schema must still yield an agy-schema marker, got %q",
result.sourceVersion)
}
// TestAntigravityCLIPBSourceVersionEmpty verifies that a legacy .pb session
// (no .db schema available) leaves SourceVersion empty rather than fabricating.
func TestAntigravityCLIPBSourceVersionEmpty(t *testing.T) {
root := t.TempDir()
id := "dddddddd-1111-2222-3333-444444444444"
mustMkdir(t, filepath.Join(root, "conversations"))
pbPath := filepath.Join(root, "conversations", id+".pb")
mustWrite(t, pbPath, []byte("encrypted-placeholder"))
mustWrite(t, filepath.Join(root, "history.jsonl"),
[]byte(`{"display":"pb prompt","timestamp":1779000000000,`+
`"workspace":"/tmp/pb-proj","conversationId":"`+id+`"}`))
sess, _, err := parseAntigravityCLITestSession(t, pbPath, "/tmp/pb-proj", "m")
require.NoError(t, err)
require.NotNil(t, sess)
assert.Empty(t, sess.SourceVersion,
"sidecar-only/.pb session must not fabricate SourceVersion")
}