Skip to content
Closed
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
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

## Testing

- After changes, run `scripts/llm.sh` and resolve failures

### Jest unit
`yarn test:jest [--config=<pathToConfigFile>] [TestPathPattern]`

Expand Down
24 changes: 24 additions & 0 deletions scripts/llm.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
# Run the LLM "quick-but-wide" checks for the AI loop: minimal test coverage
# that still catches common issues (see src/dev/run_llm.ts for details).
# This stays a bash script because the sandbox may not have Node initialized
# yet; the wrapper selects the runtime before invoking the Node entrypoint.
set -euo pipefail

if command -v nvm >/dev/null 2>&1; then
nvm use >/dev/null
elif command -v fnm >/dev/null 2>&1; then
# For all my 🐟 friends
fnm use >/dev/null
else
echo "nvm or fnm not found; install one." >&2
exit 1
fi

if command -v yarn >/dev/null 2>&1; then
node --no-experimental-require-module -e "process.argv[1]=require('path').resolve('scripts/llm.sh'); require('@kbn/setup-node-env'); require('./src/dev/run_llm');"
exit 0
fi

echo "yarn not found; install yarn." >&2
exit 1
121 changes: 121 additions & 0 deletions src/dev/run_llm.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

// Avoid running the CLI when importing run_llm.
jest.mock('@kbn/dev-cli-runner', () => ({
run: jest.fn(),
}));

import Path from 'path';

import { REPO_ROOT } from '@kbn/repo-info';
import type { JestConfigEntry, JestPolicy, JestRunTarget } from './run_llm';
import { buildJestTask, getJestRunTargets } from './run_llm';

const createConfigEntry = (repoRel: string, type: JestConfigEntry['type']): JestConfigEntry => {
const absPath = Path.resolve(REPO_ROOT, repoRel);
return {
absPath,
repoRel: Path.relative(REPO_ROOT, absPath),
dir: Path.dirname(absPath),
type,
};
};

describe('run_llm helpers', () => {
describe('getJestRunTargets()', () => {
it('matches changed files to the nearest jest config and marks full runs', () => {
const parentConfig = createConfigEntry('src/dev/jest.config.js', 'unit');
const childConfig = createConfigEntry('src/dev/subdir/jest.config.js', 'unit');

const changedFiles = [
'src/dev/subdir/foo.test.ts',
'src/dev/other.test.ts',
parentConfig.repoRel,
];

const targets = getJestRunTargets(changedFiles, [parentConfig, childConfig]);

expect(targets).toHaveLength(2);

const childTarget = targets.find((target) => target.config.repoRel === childConfig.repoRel);
const parentTarget = targets.find((target) => target.config.repoRel === parentConfig.repoRel);

expect(childTarget).toBeDefined();
expect(parentTarget).toBeDefined();

if (!childTarget || !parentTarget) {
throw new Error('Expected jest targets to be defined.');
}

expect(Array.from(childTarget.files).sort()).toEqual(['src/dev/subdir/foo.test.ts']);
expect(childTarget.fullRun).toBe(false);

expect(Array.from(parentTarget.files).sort()).toEqual([
parentConfig.repoRel,
'src/dev/other.test.ts',
]);
expect(parentTarget.fullRun).toBe(true);
});
});

describe('buildJestTask()', () => {
it('builds a unit jest task with findRelatedTests and runInBand', () => {
const config = createConfigEntry('src/dev/jest.config.js', 'unit');
const target: JestRunTarget = {
config,
files: new Set(['src/dev/foo.test.ts']),
fullRun: false,
};
const policy: JestPolicy = {
passWithNoTests: true,
testEnvironmentOptions: JSON.stringify({ globalsCleanup: 'off' }),
};

const task = buildJestTask(target, 1, policy);

expect(task.command).toBe(process.execPath);
expect(task.args).toEqual([
'--no-experimental-require-module',
'scripts/jest',
'--config',
config.repoRel,
'--testEnvironmentOptions',
policy.testEnvironmentOptions,
'--runInBand',
'--findRelatedTests',
'src/dev/foo.test.ts',
'--passWithNoTests',
]);
expect(task.label).toBe(`Running Jest for ${config.repoRel} (findRelatedTests)...`);
});

it('builds an integration jest task without findRelatedTests for full runs', () => {
const config = createConfigEntry('src/dev/jest.integration.config.js', 'integration');
const target: JestRunTarget = {
config,
files: new Set(['src/dev/bar.test.ts']),
fullRun: true,
};
const policy: JestPolicy = {
passWithNoTests: true,
};

const task = buildJestTask(target, 4, policy);

expect(task.args).toEqual([
'--no-experimental-require-module',
'scripts/jest_integration',
'--config',
config.repoRel,
]);
expect(task.label).toBe(`Running Jest for ${config.repoRel}...`);
});
});
});
Loading
Loading