Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/core/import-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ export async function importFromContent(
content: string,
opts: { noEmbed?: boolean } = {},
): Promise<ImportResult> {
// Normalize slug to lowercase to match engine-level validateSlug behavior.
// Without this, putPage stores the page at the lowercased slug while the
// followup tx.upsertChunks/getTags/addTag lookups use the caller's raw
// (possibly uppercase) slug and fail with "Page not found: <slug>".
slug = slug.toLowerCase();

// Reject oversized payloads before any parsing, chunking, or embedding happens.
// Uses Buffer.byteLength to count UTF-8 bytes the same way disk size would,
// so the network path behaves identically to the file path.
Expand Down
29 changes: 29 additions & 0 deletions test/import-file.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,35 @@ Content to chunk but not embed.
expect(result.status).toBe('imported');
});

test('lowercases caller-supplied slug so putPage and upsertChunks agree', async () => {
// Engine-level validateSlug lowercases, so putPage stored the page at the
// lowercased slug. If importFromContent leaves the caller's casing intact,
// the same-transaction tx.upsertChunks/getTags calls look up the page by
// the raw uppercase slug and throw "Page not found". See issue #200.
const content = `---
type: concept
title: TestUpper
---

Content that should succeed despite uppercase slug input.
`;

const engine = mockEngine();
const result = await importFromContent(engine, 'claude-memory/TestUpper/test', content, { noEmbed: true });

expect(result.status).toBe('imported');
expect(result.slug).toBe('claude-memory/testupper/test');

const calls = (engine as any)._calls;
const putCall = calls.find((c: any) => c.method === 'putPage');
expect(putCall).toBeTruthy();
expect(putCall.args[0]).toBe('claude-memory/testupper/test');

const chunkCall = calls.find((c: any) => c.method === 'upsertChunks');
expect(chunkCall).toBeTruthy();
expect(chunkCall.args[0]).toBe('claude-memory/testupper/test');
});

test('assigns sequential chunk_index values', async () => {
const filePath = join(TMP, 'indexed.md');
const longText = Array(50).fill('This is a sentence that adds length to the content.').join(' ');
Expand Down
Loading