Skip to content
Open
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
67 changes: 67 additions & 0 deletions core/test/artifacts/artifact_service_test_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ import {BaseArtifactService, CompositeSessionKey} from '@google/adk';
import {Part} from '@google/genai';
import {afterEach, beforeEach, describe, expect, it} from 'vitest';

/**
* Legal-but-unusual artifact filenames that every backend must handle
* identically. Leading/trailing whitespace and `..`-prefixed names are
* deliberately absent: the backends genuinely disagree on those today.
*/
const FILENAME_EDGE_CASES: Array<[label: string, filename: string]> = [
['a leading dot', '.hidden.txt'],
['an interior space', 'my report.txt'],
['a trailing dot', 'trailing.dot.'],
['a nested path', 'nested/dir/report.txt'],
];

/**
* Runs the shared artifact service tests.
*
Expand Down Expand Up @@ -246,6 +258,61 @@ export function runArtifactServiceTests(
});
});

describe('filename edge cases', () => {
it.each(FILENAME_EDGE_CASES)(
'round-trips a filename with %s',
async (_label, filename) => {
const text = `content of ${filename}`;

const version = await service.saveArtifact({
appName,
userId,
sessionId,
filename,
artifact: {text},
});
expect(version).toBe(0);

const loaded = await service.loadArtifact({
appName,
userId,
sessionId,
filename,
});
expect(loaded?.text).toBe(text);

const keys = await service.listArtifactKeys({
appName,
userId,
sessionId,
});
expect(keys).toContain(filename);
},
);

it('lists every edge-case filename verbatim and keeps them distinct', async () => {
for (const [, filename] of FILENAME_EDGE_CASES) {
await service.saveArtifact({
appName,
userId,
sessionId,
filename,
artifact: {text: filename},
});
}

const keys = await service.listArtifactKeys({
appName,
userId,
sessionId,
});

expect([...keys].sort()).toEqual(
FILENAME_EDGE_CASES.map(([, filename]) => filename).sort(),
);
});
});

describe('deleteArtifact', () => {
it('deletes an artifact', async () => {
const filename = 'del.txt';
Expand Down
Loading