kenn-io--agentsview
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
573 行
16 KiB
Go
573 行
16 KiB
Go
package importer
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"go.kenn.io/agentsview/internal/db"
|
|
"go.kenn.io/agentsview/internal/dbtest"
|
|
)
|
|
|
|
const testConversationsJSON = `[
|
|
{
|
|
"uuid": "import-test-001",
|
|
"name": "First Chat",
|
|
"summary": "",
|
|
"created_at": "2026-02-01T09:00:00.000000Z",
|
|
"updated_at": "2026-02-01T09:15:00.000000Z",
|
|
"account": {"uuid": "acct-1"},
|
|
"chat_messages": [
|
|
{
|
|
"uuid": "m1",
|
|
"text": "Hello",
|
|
"content": [{"type":"text","text":"Hello"}],
|
|
"sender": "human",
|
|
"created_at": "2026-02-01T09:00:00.000000Z",
|
|
"updated_at": "2026-02-01T09:00:00.000000Z",
|
|
"attachments": [],
|
|
"files": []
|
|
},
|
|
{
|
|
"uuid": "m2",
|
|
"text": "Hi there!",
|
|
"content": [{"type":"text","text":"Hi there!"}],
|
|
"sender": "assistant",
|
|
"created_at": "2026-02-01T09:00:05.000000Z",
|
|
"updated_at": "2026-02-01T09:00:05.000000Z",
|
|
"attachments": [],
|
|
"files": []
|
|
}
|
|
]
|
|
}
|
|
]`
|
|
|
|
const testConversationsWithAttachmentJSON = `[{
|
|
"uuid": "import-test-002",
|
|
"name": "Attachment Chat",
|
|
"summary": "",
|
|
"created_at": "2026-02-02T09:00:00.000000Z",
|
|
"updated_at": "2026-02-02T09:15:00.000000Z",
|
|
"account": {"uuid":"acct-1"},
|
|
"chat_messages": [
|
|
{
|
|
"uuid":"m1",
|
|
"text":"Can you show me the config?",
|
|
"content":[{"type":"text","text":"Can you show me the config?"}],
|
|
"sender":"human",
|
|
"created_at":"2026-02-02T09:00:00.000000Z",
|
|
"updated_at":"2026-02-02T09:00:00.000000Z",
|
|
"attachments":[],
|
|
"files":[]
|
|
},
|
|
{
|
|
"uuid":"m2",
|
|
"text":"Sure, here it is.",
|
|
"content":[{"type":"text","text":"Sure, here it is."}],
|
|
"sender":"assistant",
|
|
"created_at":"2026-02-02T09:00:05.000000Z",
|
|
"updated_at":"2026-02-02T09:00:05.000000Z",
|
|
"attachments":[
|
|
{
|
|
"file_name":"agent.yaml",
|
|
"extracted_content":"model: claude-3.7\nmode: debug"
|
|
}
|
|
],
|
|
"files":[]
|
|
}
|
|
]
|
|
}]`
|
|
|
|
const testConversationsWithoutAttachmentJSON = `[{
|
|
"uuid": "import-test-002",
|
|
"name": "Attachment Chat",
|
|
"summary": "",
|
|
"created_at": "2026-02-02T09:00:00.000000Z",
|
|
"updated_at": "2026-02-02T09:15:00.000000Z",
|
|
"account": {"uuid":"acct-1"},
|
|
"chat_messages": [
|
|
{
|
|
"uuid":"m1",
|
|
"text":"Can you show me the config?",
|
|
"content":[{"type":"text","text":"Can you show me the config?"}],
|
|
"sender":"human",
|
|
"created_at":"2026-02-02T09:00:00.000000Z",
|
|
"updated_at":"2026-02-02T09:00:00.000000Z",
|
|
"attachments":[],
|
|
"files":[]
|
|
},
|
|
{
|
|
"uuid":"m2",
|
|
"text":"Sure, here it is.",
|
|
"content":[{"type":"text","text":"Sure, here it is."}],
|
|
"sender":"assistant",
|
|
"created_at":"2026-02-02T09:00:05.000000Z",
|
|
"updated_at":"2026-02-02T09:00:05.000000Z",
|
|
"attachments":[],
|
|
"files":[]
|
|
}
|
|
]
|
|
}]`
|
|
|
|
func testDB(t *testing.T) *db.DB {
|
|
t.Helper()
|
|
return dbtest.OpenTestDB(t)
|
|
}
|
|
|
|
func TestImportClaudeAI(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
stats, err := ImportClaudeAI(
|
|
ctx, d, strings.NewReader(testConversationsJSON), nil, "workstation",
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, stats.Imported)
|
|
assert.Equal(t, 0, stats.Updated)
|
|
|
|
s, err := d.GetSession(ctx, "claude-ai:import-test-001")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, s)
|
|
assert.Equal(t, "claude.ai", s.Project)
|
|
assert.Equal(t, "claude-ai", s.Agent)
|
|
assert.Equal(t, "workstation", s.Machine)
|
|
require.NotNil(t, s.DisplayName)
|
|
assert.Equal(t, "First Chat", *s.DisplayName)
|
|
|
|
msgs, err := d.GetAllMessages(ctx, "claude-ai:import-test-001")
|
|
require.NoError(t, err)
|
|
assert.Len(t, msgs, 2)
|
|
}
|
|
|
|
func TestImportClaudeAIIncludesAttachmentContent(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
stats, err := ImportClaudeAI(
|
|
ctx, d, strings.NewReader(testConversationsWithAttachmentJSON), nil,
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, stats.Imported)
|
|
assert.Equal(t, 0, stats.Updated)
|
|
|
|
msgs, err := d.GetAllMessages(
|
|
ctx, "claude-ai:import-test-002",
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, msgs, 2)
|
|
assert.Equal(t, "Can you show me the config?", msgs[0].Content)
|
|
assert.Equal(
|
|
t,
|
|
"Sure, here it is.\n\n[Attachment: agent.yaml]\nmodel: claude-3.7\nmode: debug",
|
|
msgs[1].Content,
|
|
)
|
|
}
|
|
|
|
func TestImportClaudeAIReimportRefreshesAttachmentContent(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
stats, err := ImportClaudeAI(
|
|
ctx, d, strings.NewReader(testConversationsWithoutAttachmentJSON), nil,
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, stats.Imported)
|
|
|
|
msgs, err := d.GetAllMessages(
|
|
ctx, "claude-ai:import-test-002",
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, msgs, 2)
|
|
assert.Equal(t, "Sure, here it is.", msgs[1].Content)
|
|
|
|
stats, err = ImportClaudeAI(
|
|
ctx, d, strings.NewReader(testConversationsWithAttachmentJSON), nil,
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, stats.Imported)
|
|
assert.Equal(t, 1, stats.Updated)
|
|
assert.Equal(t, 0, stats.Skipped)
|
|
|
|
msgs, err = d.GetAllMessages(
|
|
ctx, "claude-ai:import-test-002",
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, msgs, 2)
|
|
assert.Equal(
|
|
t,
|
|
"Sure, here it is.\n\n[Attachment: agent.yaml]\nmodel: claude-3.7\nmode: debug",
|
|
msgs[1].Content,
|
|
)
|
|
}
|
|
|
|
func TestImportClaudeAI_ReimportSkipsUnchanged(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
_, err := ImportClaudeAI(
|
|
ctx, d, strings.NewReader(testConversationsJSON), nil,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Re-importing the same file skips conversations whose
|
|
// message count has not changed.
|
|
stats, err := ImportClaudeAI(
|
|
ctx, d, strings.NewReader(testConversationsJSON), nil,
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, stats.Imported)
|
|
assert.Equal(t, 0, stats.Updated)
|
|
assert.Equal(t, 1, stats.Skipped)
|
|
|
|
// Messages are still intact.
|
|
msgs, err := d.GetAllMessages(
|
|
ctx, "claude-ai:import-test-001",
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Len(t, msgs, 2)
|
|
}
|
|
|
|
func TestImportClaudeAI_PreservesDisplayNameOnReimport(
|
|
t *testing.T,
|
|
) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
_, err := ImportClaudeAI(
|
|
ctx, d, strings.NewReader(testConversationsJSON), nil,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
newName := "My Custom Name"
|
|
err = d.RenameSession(
|
|
"claude-ai:import-test-001", &newName,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
_, err = ImportClaudeAI(
|
|
ctx, d, strings.NewReader(testConversationsJSON), nil,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
s, err := d.GetSession(ctx, "claude-ai:import-test-001")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, s)
|
|
require.NotNil(t, s.DisplayName)
|
|
assert.Equal(t, "My Custom Name", *s.DisplayName)
|
|
}
|
|
|
|
const testChatGPTConv = `[{
|
|
"id":"cg-1","conversation_id":"cg-1","title":"Test",
|
|
"create_time":1706745600.0,"update_time":1706745660.0,
|
|
"current_node":"n1","mapping":{
|
|
"r":{"id":"r","parent":null,"children":["n1"],
|
|
"message":null},
|
|
"n1":{"id":"n1","parent":"r","children":[],"message":{
|
|
"id":"m1","create_time":1706745600.0,
|
|
"author":{"role":"user","name":null,"metadata":{}},
|
|
"content":{"content_type":"text","parts":["Hello"]},
|
|
"status":"finished_successfully","metadata":{}}}
|
|
}
|
|
}]`
|
|
|
|
func TestImportChatGPT(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
dir := t.TempDir()
|
|
require.NoError(t, os.WriteFile(
|
|
filepath.Join(dir, "conversations-000.json"),
|
|
[]byte(testChatGPTConv), 0o644,
|
|
))
|
|
assetsDir := filepath.Join(t.TempDir(), "assets")
|
|
|
|
stats, err := ImportChatGPT(
|
|
ctx, d, dir, assetsDir, nil, "workstation",
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, stats.Imported)
|
|
assert.Equal(t, 0, stats.Skipped)
|
|
|
|
s, err := d.GetSession(ctx, "chatgpt:cg-1")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, s)
|
|
assert.Equal(t, "chatgpt.com", s.Project)
|
|
assert.Equal(t, "workstation", s.Machine)
|
|
}
|
|
|
|
func TestImportChatGPTSanitizesParserRows(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
longModel := strings.Repeat("m", db.MaxModelLen+16)
|
|
conv := `[{
|
|
"id":"cg-sanitize","conversation_id":"cg-sanitize",
|
|
"title":"Sanitize","create_time":7258118400.0,
|
|
"update_time":7258118460.0,"current_node":"n2",
|
|
"mapping":{
|
|
"r":{"id":"r","parent":null,"children":["n1"],"message":null},
|
|
"n1":{"id":"n1","parent":"r","children":["n2"],"message":{
|
|
"id":"m1","create_time":7258118400.0,
|
|
"author":{"role":"user","name":null,"metadata":{}},
|
|
"content":{"content_type":"text","parts":["Hello\u0000\u001b[31m"]},
|
|
"status":"finished_successfully","metadata":{}}},
|
|
"n2":{"id":"n2","parent":"n1","children":[],"message":{
|
|
"id":"m2","create_time":7258118401.0,
|
|
"author":{"role":"assistant","name":null,"metadata":{}},
|
|
"content":{"content_type":"text","parts":["Hi"]},
|
|
"status":"finished_successfully",
|
|
"metadata":{"model_slug":"` + longModel + `"}}}
|
|
}
|
|
}]`
|
|
|
|
dir := t.TempDir()
|
|
require.NoError(t, os.WriteFile(
|
|
filepath.Join(dir, "conversations-000.json"),
|
|
[]byte(conv), 0o644,
|
|
))
|
|
assetsDir := filepath.Join(t.TempDir(), "assets")
|
|
|
|
stats, err := ImportChatGPT(ctx, d, dir, assetsDir, nil)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, stats.Imported)
|
|
|
|
s, err := d.GetSession(ctx, "chatgpt:cg-sanitize")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, s)
|
|
assert.Nil(t, s.StartedAt)
|
|
assert.Nil(t, s.EndedAt)
|
|
|
|
msgs, err := d.GetAllMessages(ctx, "chatgpt:cg-sanitize")
|
|
require.NoError(t, err)
|
|
require.Len(t, msgs, 2)
|
|
assert.Equal(t, "Hello[31m", msgs[0].Content)
|
|
assert.Empty(t, msgs[0].Timestamp)
|
|
assert.Empty(t, msgs[1].Timestamp)
|
|
assert.Len(t, msgs[1].Model, db.MaxModelLen)
|
|
}
|
|
|
|
func TestImportAdvancesLocalModifiedAt(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
_, err := ImportClaudeAI(
|
|
ctx, d, strings.NewReader(testConversationsJSON), nil,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// local_modified_at must be non-NULL after import so incremental PG push
|
|
// picks up session_name changes without relying on file_mtime.
|
|
// In practice this is set by replaceSecretFindingsTx which is called
|
|
// inside ReplaceSessionMessages on every message-replacing import.
|
|
full, err := d.GetSessionFull(ctx, "claude-ai:import-test-001")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, full)
|
|
require.NotNil(t, full.LocalModifiedAt,
|
|
"local_modified_at must be set after import so PG push picks up session_name changes")
|
|
}
|
|
|
|
func TestImportSkipPathBumpsLocalModifiedAt(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
// First import — establishes session_name and local_modified_at.
|
|
_, err := ImportClaudeAI(ctx, d, strings.NewReader(testConversationsJSON), nil)
|
|
require.NoError(t, err)
|
|
|
|
full1, err := d.GetSessionFull(ctx, "claude-ai:import-test-001")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, full1.LocalModifiedAt)
|
|
t1 := *full1.LocalModifiedAt
|
|
|
|
// Ensure wall-clock advances so a bumped timestamp is detectably later.
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
// Re-import with same messages but a different name. Message count and
|
|
// ended_at are unchanged, so upsertConversation takes the skip path and
|
|
// returns importSkipped without calling ReplaceSessionMessages.
|
|
renamed := strings.ReplaceAll(testConversationsJSON, `"First Chat"`, `"Renamed Chat"`)
|
|
_, err = ImportClaudeAI(ctx, d, strings.NewReader(renamed), nil)
|
|
require.NoError(t, err)
|
|
|
|
full2, err := d.GetSessionFull(ctx, "claude-ai:import-test-001")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, full2.LocalModifiedAt)
|
|
t2 := *full2.LocalModifiedAt
|
|
|
|
// local_modified_at must be bumped on the skip path so incremental PG
|
|
// push picks up the session_name change.
|
|
assert.True(t, t2 > t1,
|
|
"local_modified_at must advance on skip-path reimport (t1=%s t2=%s)", t1, t2)
|
|
|
|
// Confirm session_name was also updated.
|
|
s, err := d.GetSession(ctx, "claude-ai:import-test-001")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, s.DisplayName)
|
|
assert.Equal(t, "Renamed Chat", *s.DisplayName)
|
|
}
|
|
|
|
func TestImportSetsDisplayName(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
stats, err := ImportClaudeAI(
|
|
ctx, d, strings.NewReader(testConversationsJSON), nil,
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, stats.Imported)
|
|
|
|
s, err := d.GetSession(ctx, "claude-ai:import-test-001")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, s)
|
|
require.NotNil(t, s.DisplayName)
|
|
assert.Equal(t, "First Chat", *s.DisplayName)
|
|
}
|
|
|
|
func TestImportChatGPT_UpdatesSessionNameOnReimport(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
dir := t.TempDir()
|
|
require.NoError(t, os.WriteFile(
|
|
filepath.Join(dir, "conversations-000.json"),
|
|
[]byte(testChatGPTConv), 0o644,
|
|
))
|
|
assetsDir := filepath.Join(t.TempDir(), "assets")
|
|
|
|
// First import.
|
|
_, err := ImportChatGPT(ctx, d, dir, assetsDir, nil)
|
|
require.NoError(t, err)
|
|
|
|
// testChatGPTConv has title "Test" and id "cg-1".
|
|
s, err := d.GetSession(ctx, "chatgpt:cg-1")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, s)
|
|
require.NotNil(t, s.DisplayName)
|
|
assert.Equal(t, "Test", *s.DisplayName)
|
|
|
|
// Re-import with updated title.
|
|
updated := strings.ReplaceAll(testChatGPTConv, `"title":"Test"`, `"title":"Renamed GPT Session"`)
|
|
require.NoError(t, os.WriteFile(
|
|
filepath.Join(dir, "conversations-000.json"),
|
|
[]byte(updated), 0o644,
|
|
))
|
|
_, err = ImportChatGPT(ctx, d, dir, assetsDir, nil)
|
|
require.NoError(t, err)
|
|
|
|
// session_name must be updated even though messages are skipped.
|
|
s, err = d.GetSession(ctx, "chatgpt:cg-1")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, s.DisplayName)
|
|
assert.Equal(t, "Renamed GPT Session", *s.DisplayName,
|
|
"session_name should be refreshed on ChatGPT re-import")
|
|
}
|
|
|
|
func TestImportChatGPTSanitizesSessionNameOnReimport(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
dir := t.TempDir()
|
|
require.NoError(t, os.WriteFile(
|
|
filepath.Join(dir, "conversations-000.json"),
|
|
[]byte(testChatGPTConv), 0o644,
|
|
))
|
|
assetsDir := filepath.Join(t.TempDir(), "assets")
|
|
|
|
_, err := ImportChatGPT(ctx, d, dir, assetsDir, nil)
|
|
require.NoError(t, err)
|
|
|
|
dirty := strings.ReplaceAll(
|
|
testChatGPTConv,
|
|
`"title":"Test"`,
|
|
`"title":"Renamed\u0000\u001b[31m"`,
|
|
)
|
|
require.NoError(t, os.WriteFile(
|
|
filepath.Join(dir, "conversations-000.json"),
|
|
[]byte(dirty), 0o644,
|
|
))
|
|
|
|
_, err = ImportChatGPT(ctx, d, dir, assetsDir, nil)
|
|
require.NoError(t, err)
|
|
|
|
s, err := d.GetSession(ctx, "chatgpt:cg-1")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, s)
|
|
require.NotNil(t, s.DisplayName)
|
|
assert.Equal(t, "Renamed[31m", *s.DisplayName)
|
|
}
|
|
|
|
func TestImportChatGPT_ReimportPreservesExistingFields(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
dir := t.TempDir()
|
|
require.NoError(t, os.WriteFile(
|
|
filepath.Join(dir, "conversations-000.json"),
|
|
[]byte(testChatGPTConv), 0o644,
|
|
))
|
|
assetsDir := filepath.Join(t.TempDir(), "assets")
|
|
|
|
_, err := ImportChatGPT(ctx, d, dir, assetsDir, nil)
|
|
require.NoError(t, err)
|
|
|
|
// Capture original fields.
|
|
orig, err := d.GetSession(ctx, "chatgpt:cg-1")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, orig)
|
|
origFirstMsg := orig.FirstMessage
|
|
origStarted := orig.StartedAt
|
|
origEnded := orig.EndedAt
|
|
origMsgCount := orig.MessageCount
|
|
|
|
// Re-import with only the title changed.
|
|
renamed := strings.ReplaceAll(testChatGPTConv, `"title":"Test"`, `"title":"New Title"`)
|
|
require.NoError(t, os.WriteFile(
|
|
filepath.Join(dir, "conversations-000.json"),
|
|
[]byte(renamed), 0o644,
|
|
))
|
|
_, err = ImportChatGPT(ctx, d, dir, assetsDir, nil)
|
|
require.NoError(t, err)
|
|
|
|
after, err := d.GetSession(ctx, "chatgpt:cg-1")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, after)
|
|
|
|
// session_name updated.
|
|
require.NotNil(t, after.DisplayName)
|
|
assert.Equal(t, "New Title", *after.DisplayName)
|
|
|
|
// All other fields preserved.
|
|
assert.Equal(t, origFirstMsg, after.FirstMessage, "first_message must not change")
|
|
assert.Equal(t, origStarted, after.StartedAt, "started_at must not change")
|
|
assert.Equal(t, origEnded, after.EndedAt, "ended_at must not change")
|
|
assert.Equal(t, origMsgCount, after.MessageCount, "message_count must not change")
|
|
}
|
|
|
|
func TestImportChatGPT_SkipsExisting(t *testing.T) {
|
|
d := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
dir := t.TempDir()
|
|
require.NoError(t, os.WriteFile(
|
|
filepath.Join(dir, "conversations-000.json"),
|
|
[]byte(testChatGPTConv), 0o644,
|
|
))
|
|
assetsDir := filepath.Join(t.TempDir(), "assets")
|
|
|
|
_, err := ImportChatGPT(ctx, d, dir, assetsDir, nil)
|
|
require.NoError(t, err)
|
|
|
|
stats, err := ImportChatGPT(
|
|
ctx, d, dir, assetsDir, nil,
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 0, stats.Imported)
|
|
assert.Equal(t, 1, stats.Skipped)
|
|
}
|