diff --git a/src/utils/nativeInit.ts b/src/utils/nativeInit.ts index d53e5dc..b6435e0 100644 --- a/src/utils/nativeInit.ts +++ b/src/utils/nativeInit.ts @@ -35,14 +35,11 @@ export type NativeInitOptions = { export type NativeInitResult = { filesCreated: string[]; configPath: string; - testJsonPath?: string; }; const ENTRYPOINT_FILE = 'entrypoint.py'; const CONFIG_FILE = 'config.json'; const PAYLOAD_DIR = 'payload'; -const TESTS_DIR = 'tests'; -const TEST_FILE = 'test.json'; const SDK_CONFIG_DIR = '.datacustomcode_proj'; const SDK_CONFIG_FILE = 'sdk_config.json'; @@ -62,7 +59,6 @@ const FEATURE_TEMPLATE_DIRS = new Set(Object.values(FEATURE_TEMPLATE_MAP * 2. Write `/.datacustomcode_proj/sdk_config.json` with the package type. * 3. Generate the initial `config.json` next to the entrypoint (with sdkVersion for * scripts), then re-scan the entrypoint to fill in DLO/DMO permissions. - * 4. For function packages, best-effort generate `tests/test.json` for known features. */ export async function executeNativeInit(opts: NativeInitOptions): Promise { const { codeType, packageDir, pythonPackageLocation, pythonPackageVersion, useInFeature } = opts; @@ -90,13 +86,7 @@ export async function executeNativeInit(opts: NativeInitOptions): Promise { - const sample = sampleRequestForFeature(useInFeature); - if (!sample) { - debug('no sample test.json available for feature: %s', useInFeature); - return undefined; - } - - try { - const testsDir = path.join(path.dirname(entrypointPath), TESTS_DIR); - await fs.mkdir(testsDir, { recursive: true }); - const testJsonPath = path.join(testsDir, TEST_FILE); - await writeJson(testJsonPath, sample); - return testJsonPath; - } catch (err) { - debug('failed to write test.json: %o', err); - return undefined; - } -} - -function sampleRequestForFeature(useInFeature: string | undefined): Record | undefined { - // Mirrors Python's `_generate_model_sample_data` output for SearchIndexChunkingV1Request. - // Values come from the Pydantic field examples on `chunking.py`; pydantic coerces the - // example strings ("1.0", "8.75") to floats during model_dump. - // - // The keys below are snake_case + `__c` Salesforce field names that the wire format - // requires verbatim. Disabling camelcase here is the right tradeoff: prettier's - // `quoteProps: as-needed` reformats quoted keys back to identifiers, so we can't keep - // them quoted to satisfy eslint without also tweaking prettier config repo-wide. - if (useInFeature !== 'SearchIndexChunking') return undefined; - return JSON.parse(`{ - "input": [ - { - "text": "Online Remittance Instructions\\n\\nTransfer proceeds from the sale of your ESOP/RSUs easily.", - "metadata": { - "type": "text", - "page_number": 1, - "transcript_fields": { - "speaker": "Agent", - "start_timestamp": 1.0, - "end_timestamp": 8.75 - }, - "text_as_html": "

Online Remittance Instructions

", - "source_dmo_fields": { - "FilePath__c": "quarterly_report.pdf", - "Size__c": 1377454.0, - "ContentType__c": "pdf", - "LastModified__c": "2026-03-25T02:01:24.918000" - }, - "prepend": [ - { - "dmo_name": "udmo_1__dlm", - "field_name": "ResolvedFilePath__c", - "value": "udlo_1__dll:quarterly_report.pdf" - } - ], - "image_base64": "sample_image_base64", - "image_mime_type": "image/png", - "image_type": "diagram" - } - } - ] - }`) as Record; -} - async function pathExists(p: string): Promise { try { await fs.access(p); diff --git a/test/utils/nativeInit.test.ts b/test/utils/nativeInit.test.ts index 8871111..a585454 100644 --- a/test/utils/nativeInit.test.ts +++ b/test/utils/nativeInit.test.ts @@ -26,6 +26,9 @@ import { executeNativeInit } from '../../src/utils/nativeInit.js'; // without depending on the installed Python package, each test builds a fake // package layout in a temp dir and points init at it. +// Sample request shipped in the chunking template; init must copy it verbatim. +const TEMPLATE_TEST_JSON = { input: [{ text: 'sample text from template' }] }; + type FakePackageOptions = { scriptEntrypoint?: string; scriptConfig?: Record; @@ -83,6 +86,8 @@ async function buildFakePythonPackage(opts: FakePackageOptions = {}): Promise { expect(result.filesCreated).to.deep.equal([packageDir]); expect(result.configPath).to.equal(path.join(packageDir, 'payload', 'config.json')); - expect(result.testJsonPath).to.be.undefined; const sdkConfig = JSON.parse( await fs.readFile(path.join(packageDir, '.datacustomcode_proj', 'sdk_config.json'), 'utf8') @@ -211,7 +215,6 @@ describe('executeNativeInit', () => { }); expect(result.filesCreated).to.deep.equal([packageDir]); - expect(result.testJsonPath).to.equal(path.join(packageDir, 'payload', 'tests', 'test.json')); const sdkConfig = JSON.parse( await fs.readFile(path.join(packageDir, '.datacustomcode_proj', 'sdk_config.json'), 'utf8') @@ -228,13 +231,11 @@ describe('executeNativeInit', () => { const entrypoint = await fs.readFile(path.join(packageDir, 'payload', 'entrypoint.py'), 'utf8'); expect(entrypoint).to.include('chunking entrypoint'); - // tests/test.json should be a structured sample for SearchIndexChunking. - const sample = JSON.parse(await fs.readFile(result.testJsonPath!, 'utf8')) as { - input: Array<{ text: string; metadata?: { type?: string } }>; - }; - expect(sample.input).to.be.an('array').with.length(1); - expect(sample.input[0].text).to.be.a('string').with.length.greaterThan(0); - expect(sample.input[0].metadata?.type).to.equal('text'); + // tests/test.json is copied verbatim from the template, not regenerated. + const copied = JSON.parse( + await fs.readFile(path.join(packageDir, 'payload', 'tests', 'test.json'), 'utf8') + ) as unknown; + expect(copied).to.deep.equal(TEMPLATE_TEST_JSON); }); it('does not write test.json for an unknown feature', async () => { @@ -248,7 +249,7 @@ describe('executeNativeInit', () => { useInFeature: 'UnknownFeature', }); - expect(result.testJsonPath).to.be.undefined; + expect(result.filesCreated).to.deep.equal([packageDir]); let testsDirExists = true; try { await fs.access(path.join(packageDir, 'payload', 'tests')); @@ -269,7 +270,7 @@ describe('executeNativeInit', () => { // useInFeature intentionally omitted }); - expect(result.testJsonPath).to.be.undefined; + expect(result.filesCreated).to.deep.equal([packageDir]); // Base function entrypoint copied (no chunking overlay). const entrypoint = await fs.readFile(path.join(packageDir, 'payload', 'entrypoint.py'), 'utf8'); expect(entrypoint).to.include('def function(request, runtime: Runtime)'); @@ -294,10 +295,16 @@ describe('executeNativeInit', () => { useInFeature: 'SearchIndexChunking', }); - // Init should succeed using only the base function template; test.json is still - // sample-driven (not template-driven), so it gets written. + // Init should succeed using only the base function template. No template ships a + // tests/test.json here, and init must not invent one. expect(result.filesCreated).to.deep.equal([packageDir]); - expect(result.testJsonPath).to.equal(path.join(packageDir, 'payload', 'tests', 'test.json')); + let testJsonExists = true; + try { + await fs.access(path.join(packageDir, 'payload', 'tests', 'test.json')); + } catch { + testJsonExists = false; + } + expect(testJsonExists).to.be.false; await fs.rm(customPkg, { recursive: true, force: true }); });