package store import ( "context" "path/filepath" "testing") func TestTimestampOrderKeyPreservesRFC3339NanoPrecision(t *testing.T) { earlier, earlierOK := timestampOrderKey("2026-07-12T00:00:00.000000001Z") later, laterOK := timestampOrderKey("2026-07-12T00:00:00.000000002Z") if !earlierOK || !laterOK || earlier >= later { t.Fatalf("timestamp keys out of order: %q >= %q", earlier, later) } utc, utcOK := timestampOrderKey("2026-07-12T00:00:00Z") offset, offsetOK := timestampOrderKey("2026-07-12T02:00:00+02:00") if !utcOK || !offsetOK || utc != offset { t.Fatalf("equivalent instants have different keys: %q != %q", utc, offset) } if _, ok := timestampOrderKey("invalid"); ok { t.Fatal("invalid timestamp produced an ordering key") }} func TestSQLiteTimestampKeyOrdersLegacySubMillisecondRevisions(t *testing.T) { ctx := context.Background() st, err := Open(ctx, filepath.Join(t.TempDir(), "gitcrawl.db")) if err != nil { t.Fatalf("open store: %v", err) } defer st.Close() repoID, err := st.UpsertRepository(ctx, Repository{ Owner: "openclaw", Name: "gitcrawl", FullName: "openclaw/gitcrawl", RawJSON: "{}", UpdatedAt: "2026-07-12T00:00:00Z", }) if err != nil { t.Fatalf("repository: %v", err) } thread := Thread{ RepoID: repoID, GitHubID: "10", Number: 10, Kind: "issue", State: "open", Title: "newer", HTMLURL: "https://github.com/openclaw/gitcrawl/issues/10", LabelsJSON: "[]", AssigneesJSON: "[]", RawJSON: "{}", ContentHash: "thread", UpdatedAtGitHub: "2026-07-12T00:00:00.000000002Z", UpdatedAt: "2026-07-12T00:00:00.000000002Z", } thread.ID, err = st.UpsertThread(ctx, thread) if err != nil { t.Fatalf("thread: %v", err) } newer, err := st.UpsertThreadRevisionAndFingerprint(ctx, ThreadEvidence{Thread: thread}, "2026-07-12T00:00:01Z") if err != nil { t.Fatalf("newer revision: %v", err) } thread.Title = "stale" thread.UpdatedAtGitHub = "2026-07-12T00:00:00.000000001Z" thread.UpdatedAt = thread.UpdatedAtGitHub stale, err := st.UpsertThreadRevisionAndFingerprint(ctx, ThreadEvidence{Thread: thread}, "2026-07-12T00:00:02Z") if err != nil { t.Fatalf("stale revision: %v", err) } if _, err := st.DB().ExecContext(ctx, ` update thread_revisions set observation_sequence = 0 where id in (?, ?) `, newer.RevisionID, stale.RevisionID); err != nil { t.Fatalf("mark legacy revisions: %v", err) } var latestID int64 if err := st.DB().QueryRowContext(ctx, ` select id from thread_revisions where thread_id = ? order by observation_sequence desc, gitcrawl_timestamp_key(nullif(source_updated_at, '')) desc, id desc limit 1 `, thread.ID).Scan(&latestID); err != nil { t.Fatalf("latest revision: %v", err) } if latestID != newer.RevisionID || latestID == stale.RevisionID { t.Fatalf("latest revision = %d, newer = %d, stale = %d", latestID, newer.RevisionID, stale.RevisionID) }}
package store
import (
"context"
"path/filepath"
"testing"
)
func TestTimestampOrderKeyPreservesRFC3339NanoPrecision(t *testing.T) {
earlier, earlierOK := timestampOrderKey("2026-07-12T00:00:00.000000001Z")
later, laterOK := timestampOrderKey("2026-07-12T00:00:00.000000002Z")
if !earlierOK || !laterOK || earlier >= later {
t.Fatalf("timestamp keys out of order: %q >= %q", earlier, later)
}
utc, utcOK := timestampOrderKey("2026-07-12T00:00:00Z")
offset, offsetOK := timestampOrderKey("2026-07-12T02:00:00+02:00")
if !utcOK || !offsetOK || utc != offset {
t.Fatalf("equivalent instants have different keys: %q != %q", utc, offset)
}
if _, ok := timestampOrderKey("invalid"); ok {
t.Fatal("invalid timestamp produced an ordering key")
}
}
func TestSQLiteTimestampKeyOrdersLegacySubMillisecondRevisions(t *testing.T) {
ctx := context.Background()
st, err := Open(ctx, filepath.Join(t.TempDir(), "gitcrawl.db"))
if err != nil {
t.Fatalf("open store: %v", err)
}
defer st.Close()
repoID, err := st.UpsertRepository(ctx, Repository{
Owner: "openclaw", Name: "gitcrawl", FullName: "openclaw/gitcrawl",
RawJSON: "{}", UpdatedAt: "2026-07-12T00:00:00Z",
})
if err != nil {
t.Fatalf("repository: %v", err)
}
thread := Thread{
RepoID: repoID, GitHubID: "10", Number: 10, Kind: "issue", State: "open",
Title: "newer", HTMLURL: "https://github.com/openclaw/gitcrawl/issues/10",
LabelsJSON: "[]", AssigneesJSON: "[]", RawJSON: "{}", ContentHash: "thread",
UpdatedAtGitHub: "2026-07-12T00:00:00.000000002Z",
UpdatedAt: "2026-07-12T00:00:00.000000002Z",
}
thread.ID, err = st.UpsertThread(ctx, thread)
if err != nil {
t.Fatalf("thread: %v", err)
}
newer, err := st.UpsertThreadRevisionAndFingerprint(ctx, ThreadEvidence{Thread: thread}, "2026-07-12T00:00:01Z")
if err != nil {
t.Fatalf("newer revision: %v", err)
}
thread.Title = "stale"
thread.UpdatedAtGitHub = "2026-07-12T00:00:00.000000001Z"
thread.UpdatedAt = thread.UpdatedAtGitHub
stale, err := st.UpsertThreadRevisionAndFingerprint(ctx, ThreadEvidence{Thread: thread}, "2026-07-12T00:00:02Z")
if err != nil {
t.Fatalf("stale revision: %v", err)
}
if _, err := st.DB().ExecContext(ctx, `
update thread_revisions
set observation_sequence = 0
where id in (?, ?)
`, newer.RevisionID, stale.RevisionID); err != nil {
t.Fatalf("mark legacy revisions: %v", err)
}
var latestID int64
if err := st.DB().QueryRowContext(ctx, `
select id
from thread_revisions
where thread_id = ?
order by observation_sequence desc,
gitcrawl_timestamp_key(nullif(source_updated_at, '')) desc,
id desc
limit 1
`, thread.ID).Scan(&latestID); err != nil {
t.Fatalf("latest revision: %v", err)
}
if latestID != newer.RevisionID || latestID == stale.RevisionID {
t.Fatalf("latest revision = %d, newer = %d, stale = %d", latestID, newer.RevisionID, stale.RevisionID)
}
}