f99010fae1
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
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
591 lines
22 KiB
Go
591 lines
22 KiB
Go
package parser
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestKiroProviderSourceMethods(t *testing.T) {
|
|
root := t.TempDir()
|
|
dbPath, db := newKiroProviderSQLiteDBAt(t, root)
|
|
seedKiroSQLiteSession(
|
|
t, db, "/home/user/code/kiro-app", "sqlite-session",
|
|
readKiroFixture(t, "standard_payload.json"),
|
|
1779012000000, 1779012030000,
|
|
)
|
|
seedKiroSQLiteSession(
|
|
t, db, "/home/user/code/shadowed", "shadowed-session",
|
|
readKiroFixture(t, "standard_payload.json"),
|
|
1779012000000, 1779012040000,
|
|
)
|
|
legacyPath := filepath.Join(root, "legacy-session.jsonl")
|
|
writeSourceFile(t, legacyPath, kiroProviderJSONLFixture("Legacy question"))
|
|
writeSourceFile(t, filepath.Join(root, "legacy-session.json"),
|
|
kiroProviderMetaFixture("legacy-session", "/home/user/code/legacy"))
|
|
shadowedPath := filepath.Join(root, "shadowed-session.jsonl")
|
|
writeSourceFile(t, shadowedPath, kiroProviderJSONLFixture("Shadowed question"))
|
|
writeSourceFile(t, filepath.Join(root, "notes", "nested.jsonl"), "{}\n")
|
|
|
|
provider, ok := NewProvider(AgentKiro, ProviderConfig{
|
|
Roots: []string{root},
|
|
Machine: "devbox",
|
|
})
|
|
require.True(t, ok)
|
|
|
|
plan, err := provider.WatchPlan(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, plan.Roots, 1)
|
|
assert.Equal(t, root, plan.Roots[0].Path)
|
|
assert.False(t, plan.Roots[0].Recursive)
|
|
assert.Contains(t, plan.Roots[0].IncludeGlobs, "*.jsonl")
|
|
assert.Contains(t, plan.Roots[0].IncludeGlobs, kiroSQLiteDBName)
|
|
assert.Contains(t, plan.Roots[0].IncludeGlobs, kiroSQLiteDBName+"-*")
|
|
|
|
discovered, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, discovered, 2)
|
|
assert.Equal(t, dbPath, discovered[0].DisplayPath)
|
|
assert.Equal(t, legacyPath, discovered[1].DisplayPath)
|
|
|
|
foundSQLite, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
FullSessionID: "host~kiro:sqlite-session",
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
assert.Equal(t, KiroSQLiteVirtualPath(dbPath, "sqlite-session"), foundSQLite.DisplayPath)
|
|
assert.Equal(t, foundSQLite.DisplayPath, foundSQLite.FingerprintKey)
|
|
|
|
foundLegacy, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
RawSessionID: "legacy-session",
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
assert.Equal(t, legacyPath, foundLegacy.DisplayPath)
|
|
|
|
changed, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{Path: dbPath + "-wal", EventKind: "write", WatchRoot: root},
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, changed, 1)
|
|
assert.Equal(t, dbPath, changed[0].DisplayPath)
|
|
}
|
|
|
|
func TestKiroProviderParsePhysicalVirtualAndLegacySources(t *testing.T) {
|
|
root := t.TempDir()
|
|
dbPath, db := newKiroProviderSQLiteDBAt(t, root)
|
|
seedKiroSQLiteSession(
|
|
t, db, "/home/user/code/kiro-app", "sqlite-session",
|
|
readKiroFixture(t, "standard_payload.json"),
|
|
1779012000000, 1779012030000,
|
|
)
|
|
legacyPath := filepath.Join(root, "legacy-session.jsonl")
|
|
writeSourceFile(t, legacyPath, kiroProviderJSONLFixture("Legacy question"))
|
|
|
|
provider, ok := NewProvider(AgentKiro, ProviderConfig{
|
|
Roots: []string{root},
|
|
Machine: "devbox",
|
|
})
|
|
require.True(t, ok)
|
|
sources, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, sources, 2)
|
|
|
|
allOutcome, err := provider.Parse(context.Background(), ParseRequest{Source: sources[0]})
|
|
require.NoError(t, err)
|
|
require.True(t, allOutcome.ResultSetComplete)
|
|
require.True(t, allOutcome.ForceReplace)
|
|
require.Len(t, allOutcome.Results, 1)
|
|
assert.Equal(t, "kiro:sqlite-session", allOutcome.Results[0].Result.Session.ID)
|
|
|
|
virtualSource, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
RawSessionID: "sqlite-session",
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
oneOutcome, err := provider.Parse(context.Background(), ParseRequest{Source: virtualSource})
|
|
require.NoError(t, err)
|
|
require.True(t, oneOutcome.ResultSetComplete)
|
|
require.True(t, oneOutcome.ForceReplace)
|
|
require.Len(t, oneOutcome.Results, 1)
|
|
assert.Equal(t, "devbox", oneOutcome.Results[0].Result.Session.Machine)
|
|
|
|
legacySource, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
StoredFilePath: legacyPath,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
legacyOutcome, err := provider.Parse(context.Background(), ParseRequest{
|
|
Source: legacySource,
|
|
Fingerprint: SourceFingerprint{Hash: "legacy-hash"},
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, legacyOutcome.ResultSetComplete)
|
|
require.False(t, legacyOutcome.ForceReplace)
|
|
require.Len(t, legacyOutcome.Results, 1)
|
|
assert.Equal(t, "kiro:legacy-session", legacyOutcome.Results[0].Result.Session.ID)
|
|
assert.Equal(t, "legacy-hash", legacyOutcome.Results[0].Result.Session.File.Hash)
|
|
|
|
// Close the setup handle before deleting; Windows will not unlink a file
|
|
// this process still holds open.
|
|
require.NoError(t, db.Close())
|
|
require.NoError(t, os.Remove(dbPath))
|
|
missingOutcome, err := provider.Parse(context.Background(), ParseRequest{Source: sources[0]})
|
|
require.NoError(t, err)
|
|
assert.True(t, missingOutcome.ResultSetComplete)
|
|
// The backing DB file was deleted; preserve the stored sessions by not
|
|
// force-replacing, which would delete them from the archive.
|
|
assert.False(t, missingOutcome.ForceReplace)
|
|
assert.Equal(t, SkipNoSession, missingOutcome.SkipReason)
|
|
}
|
|
|
|
func TestKiroProviderSkipsShadowedLegacySource(t *testing.T) {
|
|
root := t.TempDir()
|
|
dbPath, db := newKiroProviderSQLiteDBAt(t, root)
|
|
seedKiroSQLiteSession(
|
|
t, db, "/home/user/code/shadowed", "shadowed-session",
|
|
readKiroFixture(t, "standard_payload.json"),
|
|
1779012000000, 1779012030000,
|
|
)
|
|
shadowedPath := filepath.Join(root, "shadowed-session.jsonl")
|
|
writeSourceFile(t, shadowedPath, kiroProviderJSONLFixture("Shadowed question"))
|
|
|
|
provider, ok := NewProvider(AgentKiro, ProviderConfig{Roots: []string{root}})
|
|
require.True(t, ok)
|
|
source, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
StoredFilePath: shadowedPath,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
|
|
outcome, err := provider.Parse(context.Background(), ParseRequest{Source: source})
|
|
require.NoError(t, err)
|
|
assert.True(t, outcome.ResultSetComplete)
|
|
assert.Equal(t, SkipNoSession, outcome.SkipReason)
|
|
assert.Empty(t, outcome.Results)
|
|
|
|
source, ok, err = provider.FindSource(context.Background(), FindSourceRequest{
|
|
FullSessionID: "host~kiro:shadowed-session",
|
|
StoredFilePath: shadowedPath,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
assert.Equal(t, KiroSQLiteVirtualPath(dbPath, "shadowed-session"), source.DisplayPath)
|
|
}
|
|
|
|
func TestKiroProviderShadowsLegacyAcrossAllRoots(t *testing.T) {
|
|
sqliteRoot := t.TempDir()
|
|
legacyRoot := t.TempDir()
|
|
dbPath, db := newKiroProviderSQLiteDBAt(t, sqliteRoot)
|
|
seedKiroSQLiteSession(
|
|
t, db, "/home/user/code/current", "shared-session",
|
|
readKiroFixture(t, "standard_payload.json"),
|
|
1779012000000, 1779012030000,
|
|
)
|
|
legacyPath := filepath.Join(legacyRoot, "legacy-storage.jsonl")
|
|
writeSourceFile(t, legacyPath, kiroProviderJSONLFixture("Legacy question"))
|
|
writeSourceFile(t, filepath.Join(legacyRoot, "legacy-storage.json"),
|
|
kiroProviderMetaFixture("shared-session", "/home/user/code/legacy"))
|
|
|
|
provider, ok := NewProvider(AgentKiro, ProviderConfig{
|
|
Roots: []string{sqliteRoot, legacyRoot},
|
|
})
|
|
require.True(t, ok)
|
|
|
|
discovered, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, discovered, 1)
|
|
assert.Equal(t, dbPath, discovered[0].DisplayPath)
|
|
|
|
legacySource, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
StoredFilePath: legacyPath,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
outcome, err := provider.Parse(context.Background(), ParseRequest{
|
|
Source: legacySource,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.True(t, outcome.ResultSetComplete)
|
|
assert.Equal(t, SkipNoSession, outcome.SkipReason)
|
|
assert.Empty(t, outcome.Results)
|
|
}
|
|
|
|
func TestKiroProviderFingerprintsSQLiteAndLegacySources(t *testing.T) {
|
|
root := t.TempDir()
|
|
payload := readKiroFixture(t, "standard_payload.json")
|
|
dbPath, db := newKiroProviderSQLiteDBAt(t, root)
|
|
seedKiroSQLiteSession(
|
|
t, db, "/home/user/code/kiro-app", "sqlite-session",
|
|
payload,
|
|
1779012000000, 1779012030000,
|
|
)
|
|
legacyPath := filepath.Join(root, "legacy-session.jsonl")
|
|
writeSourceFile(t, legacyPath, kiroProviderJSONLFixture("Legacy question"))
|
|
|
|
provider, ok := NewProvider(AgentKiro, ProviderConfig{Roots: []string{root}})
|
|
require.True(t, ok)
|
|
|
|
virtualSource, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
RawSessionID: "sqlite-session",
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
virtualFingerprint, err := provider.Fingerprint(context.Background(), virtualSource)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, KiroSQLiteVirtualPath(dbPath, "sqlite-session"), virtualFingerprint.Key)
|
|
assert.Equal(t, int64(len(payload)), virtualFingerprint.Size)
|
|
assert.Equal(t, int64(1779012030000)*1_000_000, virtualFingerprint.MTimeNS)
|
|
|
|
sources, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, sources)
|
|
sqliteSource := sources[0]
|
|
require.Equal(t, dbPath, sqliteSource.DisplayPath)
|
|
beforePhysical, err := provider.Fingerprint(context.Background(), sqliteSource)
|
|
require.NoError(t, err)
|
|
walPath := dbPath + "-wal"
|
|
writeSourceFile(t, walPath, "wal")
|
|
walTime := time.Unix(0, beforePhysical.MTimeNS+int64(time.Second))
|
|
require.NoError(t, os.Chtimes(walPath, walTime, walTime))
|
|
afterPhysical, err := provider.Fingerprint(context.Background(), sqliteSource)
|
|
require.NoError(t, err)
|
|
assert.Greater(t, afterPhysical.MTimeNS, beforePhysical.MTimeNS)
|
|
|
|
legacySource, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
StoredFilePath: legacyPath,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
legacyFingerprint, err := provider.Fingerprint(context.Background(), legacySource)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, legacyPath, legacyFingerprint.Key)
|
|
assert.NotEmpty(t, legacyFingerprint.Hash)
|
|
}
|
|
|
|
func TestKiroProviderMissingSQLiteSourcesCanReachParse(t *testing.T) {
|
|
root := t.TempDir()
|
|
dbPath, db := newKiroProviderSQLiteDBAt(t, root)
|
|
seedKiroSQLiteSession(
|
|
t, db, "/home/user/code/kiro-app", "sqlite-session",
|
|
readKiroFixture(t, "standard_payload.json"),
|
|
1779012000000, 1779012030000,
|
|
)
|
|
|
|
provider, ok := NewProvider(AgentKiro, ProviderConfig{Roots: []string{root}})
|
|
require.True(t, ok)
|
|
sources, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, sources, 1)
|
|
physicalSource := sources[0]
|
|
virtualSource, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
RawSessionID: "sqlite-session",
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
|
|
_, err = db.Exec(`DELETE FROM conversations_v2 WHERE conversation_id = ?`, "sqlite-session")
|
|
require.NoError(t, err)
|
|
_, ok, err = provider.FindSource(context.Background(), FindSourceRequest{
|
|
StoredFilePath: virtualSource.DisplayPath,
|
|
RequireFreshSource: true,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.False(t, ok, "fresh lookup must reject a deleted SQLite row")
|
|
staleVirtualSource, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
StoredFilePath: virtualSource.DisplayPath,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok, "non-fresh lookup keeps virtual tombstone identity")
|
|
assert.Equal(t, virtualSource.DisplayPath, staleVirtualSource.DisplayPath)
|
|
virtualFingerprint, err := provider.Fingerprint(context.Background(), virtualSource)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, virtualSource.FingerprintKey, virtualFingerprint.Key)
|
|
virtualOutcome, err := provider.Parse(context.Background(), ParseRequest{
|
|
Source: virtualSource,
|
|
Fingerprint: virtualFingerprint,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.True(t, virtualOutcome.ResultSetComplete)
|
|
assert.True(t, virtualOutcome.ForceReplace)
|
|
assert.Equal(t, SkipNoSession, virtualOutcome.SkipReason)
|
|
|
|
require.NoError(t, db.Close())
|
|
require.NoError(t, os.Remove(dbPath))
|
|
_, ok, err = provider.FindSource(context.Background(), FindSourceRequest{
|
|
StoredFilePath: physicalSource.DisplayPath,
|
|
RequireFreshSource: true,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.False(t, ok, "fresh lookup must reject a deleted SQLite DB")
|
|
physicalFingerprint, err := provider.Fingerprint(context.Background(), physicalSource)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, physicalSource.FingerprintKey, physicalFingerprint.Key)
|
|
physicalOutcome, err := provider.Parse(context.Background(), ParseRequest{
|
|
Source: physicalSource,
|
|
Fingerprint: physicalFingerprint,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.True(t, physicalOutcome.ResultSetComplete)
|
|
// The whole DB file was removed: preserve the stored sessions by not
|
|
// force-replacing. (The virtual case above keeps ForceReplace because the
|
|
// DB file is still present and only the row was deleted.)
|
|
assert.False(t, physicalOutcome.ForceReplace)
|
|
assert.Equal(t, SkipNoSession, physicalOutcome.SkipReason)
|
|
}
|
|
|
|
// TestKiroProviderChangedPathTombstonesDeletedRow verifies the changed-path
|
|
// classifier emits a per-session tombstone for a stored Kiro SQLite member
|
|
// whose row was deleted from a still-present database, so the engine can
|
|
// force-replace it out of the archive. The surviving member is left to the
|
|
// whole-DB fan-out, and a vanished database emits no tombstone (the stored
|
|
// sessions are preserved per the persistent-archive rule).
|
|
func TestKiroProviderChangedPathTombstonesDeletedRow(t *testing.T) {
|
|
root := t.TempDir()
|
|
dbPath, db := newKiroProviderSQLiteDBAt(t, root)
|
|
seedKiroSQLiteSession(
|
|
t, db, "/home/user/code/kiro-app", "surviving",
|
|
readKiroFixture(t, "standard_payload.json"),
|
|
1779012000000, 1779012030000,
|
|
)
|
|
seedKiroSQLiteSession(
|
|
t, db, "/home/user/code/kiro-app", "deleted",
|
|
readKiroFixture(t, "standard_payload.json"),
|
|
1779012000000, 1779012040000,
|
|
)
|
|
|
|
provider, ok := NewProvider(AgentKiro, ProviderConfig{
|
|
Roots: []string{root},
|
|
Machine: "devbox",
|
|
})
|
|
require.True(t, ok)
|
|
|
|
survivingPath := KiroSQLiteVirtualPath(dbPath, "surviving")
|
|
deletedPath := KiroSQLiteVirtualPath(dbPath, "deleted")
|
|
|
|
// Delete one row while the database file stays present.
|
|
_, err := db.Exec(`DELETE FROM conversations_v2 WHERE conversation_id = ?`, "deleted")
|
|
require.NoError(t, err)
|
|
|
|
changed, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{
|
|
Path: dbPath,
|
|
EventKind: "write",
|
|
WatchRoot: root,
|
|
StoredSourcePaths: []string{survivingPath, deletedPath},
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
gotPaths := make([]string, len(changed))
|
|
for i, src := range changed {
|
|
gotPaths[i] = src.DisplayPath
|
|
}
|
|
assert.ElementsMatch(t, []string{dbPath, deletedPath}, gotPaths,
|
|
"whole-DB source plus a tombstone for the deleted row only")
|
|
|
|
var tombstone SourceRef
|
|
for _, src := range changed {
|
|
if src.DisplayPath == deletedPath {
|
|
tombstone = src
|
|
}
|
|
}
|
|
require.NotEmpty(t, tombstone.DisplayPath, "deleted-row tombstone source")
|
|
fingerprint, err := provider.Fingerprint(context.Background(), tombstone)
|
|
require.NoError(t, err)
|
|
outcome, err := provider.Parse(context.Background(), ParseRequest{
|
|
Source: tombstone,
|
|
Fingerprint: fingerprint,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.True(t, outcome.ResultSetComplete)
|
|
assert.True(t, outcome.ForceReplace,
|
|
"a row deleted from a present DB is force-replaced out of the archive")
|
|
assert.Equal(t, SkipNoSession, outcome.SkipReason)
|
|
assert.Empty(t, outcome.Results)
|
|
|
|
// When the whole database file is gone, no tombstone is emitted so the
|
|
// stored sessions are preserved.
|
|
require.NoError(t, db.Close())
|
|
require.NoError(t, os.Remove(dbPath))
|
|
gone, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{
|
|
Path: dbPath,
|
|
EventKind: "remove",
|
|
WatchRoot: root,
|
|
StoredSourcePaths: []string{survivingPath, deletedPath},
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
for _, src := range gone {
|
|
assert.NotEqual(t, deletedPath, src.DisplayPath,
|
|
"a vanished database must not tombstone stored sessions")
|
|
assert.NotEqual(t, survivingPath, src.DisplayPath)
|
|
}
|
|
}
|
|
|
|
func TestKiroProviderRejectsInvalidStoredSQLitePaths(t *testing.T) {
|
|
root := t.TempDir()
|
|
dbPath, db := newKiroProviderSQLiteDBAt(t, root)
|
|
seedKiroSQLiteSession(
|
|
t, db, "/home/user/code/kiro-app", "sqlite-session",
|
|
readKiroFixture(t, "standard_payload.json"),
|
|
1779012000000, 1779012030000,
|
|
)
|
|
provider, ok := NewProvider(AgentKiro, ProviderConfig{Roots: []string{root}})
|
|
require.True(t, ok)
|
|
|
|
for _, path := range []string{
|
|
dbPath + "#",
|
|
filepath.Join(root, "data-copy.sqlite3") + "#sqlite-session",
|
|
filepath.Join(root, "nested", kiroSQLiteDBName) + "#sqlite-session",
|
|
} {
|
|
_, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
StoredFilePath: path,
|
|
RequireFreshSource: true,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.False(t, ok, "stored path %q", path)
|
|
}
|
|
}
|
|
|
|
func TestKiroIDEProviderSourceMethods(t *testing.T) {
|
|
root := t.TempDir()
|
|
oldWSHash := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
oldFileHash := "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
|
oldPath := filepath.Join(root, oldWSHash, oldFileHash+".chat")
|
|
writeSourceFile(t, oldPath, kiroIDEProviderOldFixture("Old IDE question"))
|
|
newPath := filepath.Join(root, "workspace-sessions", "encoded-workspace", "new-session.json")
|
|
writeSourceFile(t, newPath, kiroIDEProviderNewFixture("New IDE question"))
|
|
writeSourceFile(t, filepath.Join(root, "workspace-sessions", "encoded-workspace", "sessions.json"), "[]\n")
|
|
writeSourceFile(t, filepath.Join(root, "default", "ignored.chat"), kiroIDEProviderOldFixture("Ignored"))
|
|
|
|
provider, ok := NewProvider(AgentKiroIDE, ProviderConfig{
|
|
Roots: []string{root},
|
|
Machine: "devbox",
|
|
})
|
|
require.True(t, ok)
|
|
|
|
plan, err := provider.WatchPlan(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, plan.Roots, 1)
|
|
assert.Equal(t, root, plan.Roots[0].Path)
|
|
assert.True(t, plan.Roots[0].Recursive)
|
|
assert.Contains(t, plan.Roots[0].IncludeGlobs, "*.chat")
|
|
assert.Contains(t, plan.Roots[0].IncludeGlobs, "*.json")
|
|
|
|
discovered, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, discovered, 2)
|
|
assert.Equal(t, oldPath, discovered[0].DisplayPath)
|
|
assert.Equal(t, newPath, discovered[1].DisplayPath)
|
|
|
|
foundOld, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
RawSessionID: oldWSHash + ":" + oldFileHash,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
assert.Equal(t, oldPath, foundOld.DisplayPath)
|
|
|
|
foundNew, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
FullSessionID: "host~kiro-ide:new-session",
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
assert.Equal(t, newPath, foundNew.DisplayPath)
|
|
}
|
|
|
|
func TestKiroIDEProviderParsesOldAndNewSources(t *testing.T) {
|
|
root := t.TempDir()
|
|
oldWSHash := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
|
oldFileHash := "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
|
|
oldPath := filepath.Join(root, oldWSHash, oldFileHash+".chat")
|
|
writeSourceFile(t, oldPath, kiroIDEProviderOldFixture("Old IDE question"))
|
|
newPath := filepath.Join(root, "workspace-sessions", "encoded-workspace", "new-session.json")
|
|
writeSourceFile(t, newPath, kiroIDEProviderNewFixture("New IDE question"))
|
|
|
|
provider, ok := NewProvider(AgentKiroIDE, ProviderConfig{
|
|
Roots: []string{root},
|
|
Machine: "devbox",
|
|
})
|
|
require.True(t, ok)
|
|
sources, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, sources, 2)
|
|
|
|
oldOutcome, err := provider.Parse(context.Background(), ParseRequest{Source: sources[0]})
|
|
require.NoError(t, err)
|
|
require.True(t, oldOutcome.ResultSetComplete)
|
|
require.Len(t, oldOutcome.Results, 1)
|
|
assert.Equal(t, "kiro-ide:"+oldWSHash+":"+oldFileHash, oldOutcome.Results[0].Result.Session.ID)
|
|
assert.Equal(t, "devbox", oldOutcome.Results[0].Result.Session.Machine)
|
|
|
|
newOutcome, err := provider.Parse(context.Background(), ParseRequest{
|
|
Source: sources[1],
|
|
Fingerprint: SourceFingerprint{Hash: "new-hash"},
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, newOutcome.ResultSetComplete)
|
|
require.Len(t, newOutcome.Results, 1)
|
|
assert.Equal(t, "kiro-ide:new-session", newOutcome.Results[0].Result.Session.ID)
|
|
assert.Equal(t, "new-hash", newOutcome.Results[0].Result.Session.File.Hash)
|
|
}
|
|
|
|
func TestKiroIDEProviderFingerprintsSessionContent(t *testing.T) {
|
|
root := t.TempDir()
|
|
path := filepath.Join(root, "workspace-sessions", "encoded-workspace", "new-session.json")
|
|
writeSourceFile(t, path, kiroIDEProviderNewFixture("New IDE question"))
|
|
|
|
provider, ok := NewProvider(AgentKiroIDE, ProviderConfig{Roots: []string{root}})
|
|
require.True(t, ok)
|
|
source, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
RawSessionID: "new-session",
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
before, err := provider.Fingerprint(context.Background(), source)
|
|
require.NoError(t, err)
|
|
|
|
writeSourceFile(t, path, kiroIDEProviderNewFixture("Changed IDE question"))
|
|
after, err := provider.Fingerprint(context.Background(), source)
|
|
require.NoError(t, err)
|
|
assert.NotEqual(t, before.Hash, after.Hash)
|
|
}
|
|
|
|
func newKiroProviderSQLiteDBAt(t *testing.T, root string) (string, *sql.DB) {
|
|
t.Helper()
|
|
dbPath := filepath.Join(root, kiroSQLiteDBName)
|
|
db, err := sql.Open("sqlite3", dbPath)
|
|
require.NoError(t, err, "open kiro provider sqlite db")
|
|
t.Cleanup(func() { _ = db.Close() })
|
|
_, err = db.Exec(kiroSQLiteSchema)
|
|
require.NoError(t, err, "create kiro sqlite schema")
|
|
return dbPath, db
|
|
}
|
|
|
|
func kiroProviderJSONLFixture(question string) string {
|
|
return `{"kind":"Prompt","data":{"content":[{"kind":"text","data":"` + question + `"}]}}` + "\n" +
|
|
`{"kind":"AssistantMessage","data":{"content":[{"kind":"text","data":"Kiro answer"}]}}` + "\n"
|
|
}
|
|
|
|
func kiroProviderMetaFixture(sessionID, cwd string) string {
|
|
return `{"session_id":"` + sessionID + `","cwd":"` + cwd + `","title":"` + sessionID + `","created_at":"2026-06-01T10:00:00Z","updated_at":"2026-06-01T10:01:00Z"}` + "\n"
|
|
}
|
|
|
|
func kiroIDEProviderOldFixture(question string) string {
|
|
return `{"executionId":"exec-old","actionId":"act-old","chat":[{"role":"human","content":"` + question + `"},{"role":"bot","content":"Old IDE answer"}],"metadata":{"modelId":"claude-sonnet-4-6","startTime":1779012000000,"endTime":1779012030000}}` + "\n"
|
|
}
|
|
|
|
func kiroIDEProviderNewFixture(question string) string {
|
|
return `{"sessionId":"new-session","title":"New title","workspaceDirectory":"/home/user/dev/new-app","history":[{"message":{"role":"user","content":"` + question + `","id":"m1"}},{"message":{"role":"assistant","content":"New IDE answer","id":"m2"}}]}` + "\n"
|
|
}
|