Repository navigation
test: cover the implicit file context, isolate temp fixtures per test #1704
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| import os from 'node:os' | ||
| import { describe, expect, test } from 'vitest' | ||
| import { Template } from '../../src' | ||
|
|
||
| // Path to a committed, read-only fixture, relative to this file's directory. | ||
| // The implicit file context is the directory of the file that calls Template(), | ||
| // so it is always inside the repository — which is why the fixture is committed | ||
| // and never written to, rather than generated into a temp directory. | ||
| const fixturePath = 'fixtures/hello.txt' | ||
|
|
||
| describe('file context', () => { | ||
| test('defaults to the directory of the caller of Template()', async () => { | ||
| const implicit = Template().fromBaseImage().copy(fixturePath, 'hello.txt') | ||
| const explicit = Template({ fileContextPath: __dirname }) | ||
| .fromBaseImage() | ||
| .copy(fixturePath, 'hello.txt') | ||
|
|
||
| // toJSON hashes each COPY's files, so the two serializations only match if | ||
| // the implicit context resolved to this file's directory, the glob found | ||
| // the fixture there, and its contents were read. This is the only test that | ||
| // exercises the implicit default end to end — every other template test | ||
| // passes fileContextPath explicitly, and the unit test for | ||
| // getCallerDirectory covers the helper in isolation, not its use here. | ||
| expect(await Template.toJSON(implicit)).toBe( | ||
| await Template.toJSON(explicit) | ||
| ) | ||
| }) | ||
|
|
||
| test('fails to resolve a source that is not in the context', async () => { | ||
| // Keeps the assertion above from passing vacuously: the hashes match | ||
| // because the fixture was found, not because a missing file hashes the | ||
| // same either way. | ||
| const wrongContext = Template({ fileContextPath: os.tmpdir() }) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When another process or an earlier interrupted run leaves Useful? React with 👍 / 👎.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Confirming Codex's P2 with a reproduction, because The glob runs with The Python mirror stayed green through the same probe because it uses a fresh const emptyContext = await mkdtemp(join(tmpdir(), 'fileContext-test-'))
try {
const wrongContext = Template({ fileContextPath: emptyContext })
.fromBaseImage()
.copy(fixturePath, 'hello.txt')
await expect(Template.toJSON(wrongContext)).rejects.toThrow(/No files found/)
} finally {
await rm(emptyContext, { recursive: true, force: true })
} |
||
| .fromBaseImage() | ||
| .copy(fixturePath, 'hello.txt') | ||
|
|
||
| await expect(Template.toJSON(wrongContext)).rejects.toThrow( | ||
| /No files found/ | ||
| ) | ||
| }) | ||
| }) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Optional, and the natural place for it is this file rather than a follow-up: two arms of the very expression this file tests are still at zero. The second one I verified is genuinely reachable, and it has a user-visible consequence. $ cp packages/js-sdk/dist/index.mjs /tmp/scratch/bundled-app.mjs
$ printf '
const t = Template();
console.log(t.fileContextPath);
' >> /tmp/scratch/bundled-app.mjs
$ node /tmp/scratch/bundled-app.mjs
bundled caller -> fileContextPath = "."
cwd = /tmp/scratchSo a bundled consumer silently resolves The browser arm is the cheaper one: no test in the repo constructs a Don't mirror either of these into the Python file, though — see the note there. |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| This file is the fixture for the implicit file-context test. It is read-only: | ||
| the implicit context is the directory of the file that calls Template(), which | ||
| is inside the repository, so nothing may be written here at test time. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,24 +1,20 @@ | ||
| import { expect, test, describe, beforeAll, afterAll, beforeEach } from 'vitest' | ||
| import { expect, test, describe, afterEach, beforeEach } from 'vitest' | ||
| import { writeFile, mkdir, mkdtemp, rm } from 'fs/promises' | ||
| import { tmpdir } from 'os' | ||
| import { join, basename } from 'path' | ||
| import { getAllFilesInPath } from '../../../src/template/utils' | ||
|
|
||
| describe('getAllFilesInPath', () => { | ||
| // A temp directory, so a test run never writes into the repository tree. | ||
| // A fresh temp directory per test, so a run never writes into the repository | ||
| // tree and no fixture leaks from one test into the next. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The description's justification for this half checks out precisely, and since it is the whole reason for the change I measured it rather than trusting it. Under the default umask So the old hook really did discard Small wording note on the comment itself: "no fixture leaks from one test into the next" was already true before, since the old No coverage or inventory movement from this half, as expected: all 17 tests here and all 7 in |
||
| let testDir: string | ||
|
|
||
| beforeAll(async () => { | ||
| beforeEach(async () => { | ||
| testDir = await mkdtemp(join(tmpdir(), 'getAllFilesInPath-test-')) | ||
| }) | ||
|
|
||
| afterAll(async () => { | ||
| await rm(testDir, { recursive: true, force: true }) | ||
| }) | ||
|
|
||
| beforeEach(async () => { | ||
| afterEach(async () => { | ||
| await rm(testDir, { recursive: true, force: true }) | ||
| await mkdir(testDir, { recursive: true }) | ||
| }) | ||
|
|
||
| test('should return files matching a simple pattern', async () => { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| This file is the fixture for the implicit file-context test. It is read-only: | ||
| the implicit context is the directory of the file that calls Template(), which | ||
| is inside the repository, so nothing may be written here at test time. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import os | ||
| import tempfile | ||
|
|
||
| import pytest | ||
|
|
||
| from e2b import Template | ||
|
|
||
| # Path to a committed, read-only fixture, relative to this file's directory. | ||
| # The implicit file context is the directory of the file that calls Template(), | ||
| # so it is always inside the repository — which is why the fixture is committed | ||
| # and never written to, rather than generated into a temp directory. | ||
| FIXTURE_PATH = "fixtures/hello.txt" | ||
|
|
||
|
|
||
| def test_file_context_defaults_to_caller_directory(): | ||
| implicit = Template().from_base_image().copy(FIXTURE_PATH, "hello.txt") | ||
| explicit = ( | ||
| Template(file_context_path=os.path.dirname(__file__)) | ||
| .from_base_image() | ||
| .copy(FIXTURE_PATH, "hello.txt") | ||
| ) | ||
|
|
||
| # to_json hashes each COPY's files, so the two serializations only match if | ||
| # the implicit context resolved to this file's directory, the glob found the | ||
| # fixture there, and its contents were read. This is the only test that | ||
| # exercises the implicit default end to end — every other template test | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same wording point as the JS twin: On the coverage side, this test contributes exactly one newly executed python line: One thing I'd explicitly not ask for here, since it looks like an obvious symmetry with the JS gap: the >>> _is_user_file('<string>')
True
>>> exec(compile('from e2b import Template
ctx = Template()._file_context_path', '<string>', 'exec'), ns)
>>> ns['ctx']
'/workspace/packages/python-sdk' # cwd, via the normal path — never the `or "."`
|
||
| # passes file_context_path explicitly, and the unit test for | ||
| # get_caller_directory covers the helper in isolation, not its use here. | ||
| assert Template.to_json(implicit) == Template.to_json(explicit) | ||
|
|
||
|
|
||
| def test_file_context_without_the_source_fails_to_resolve_it(): | ||
| # Keeps the assertion above from passing vacuously: the hashes match because | ||
| # the fixture was found, not because a missing file hashes the same either | ||
| # way. | ||
| with tempfile.TemporaryDirectory() as empty_context: | ||
| wrong_context = ( | ||
| Template(file_context_path=empty_context) | ||
| .from_base_image() | ||
| .copy(FIXTURE_PATH, "hello.txt") | ||
| ) | ||
|
|
||
| with pytest.raises(ValueError, match="No files found"): | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Non-blocking, and the cause is pre-existing. TASTE wants builder configuration failures to raise The reason to mention it on this line: the two new tests pin it asymmetrically. JS asserts only the message and would survive a fix; this one asserts the class, and |
||
| Template.to_json(wrong_context) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is the one thing here I'd change, because it will outlive the PR description and it is what a future reader will trust when deciding whether the implicit default is covered.
"This is the only test that exercises the implicit default end to end — every other template test passes
fileContextPathexplicitly" is not quite right.stacktrace.test.tsbuilds a bareTemplate()and copies real sibling files through the implicit context:I confirmed it by mutation rather than by reading: applying the PR's own mutant (
getCallerDirectory()dropped from the constructor) fails two pre-existing tests in that file —traces on step after multi-source copyandtraces on step after copyItems— withNo files found in stacktrace.test.ts.That is still an argument for this test, just a different one. Those two catches are incidental, their failure surfaces inside a stack-trace assertion that points nowhere near the file context, and the two neighbouring cases (
multiSourceCopySecondSource,copyItemsSecondItem) pass vacuously under the same mutant because they expect a copy-step failure anyway and cannot tell a mocked build error from a mis-resolved context. So the accurate claim is that this is the first test to assert the implicit context resolves to the caller's directory, rather than the only one to execute that path.Worth also noting what the coverage delta actually shows: this test is the first caller of
Template.toJSONin the whole js-sdk suite. The publicstatic toJSON(89–94) and the privateTemplateBase.toJSON(973–980) both had hit count 0 before this PR, and they are the entire js-sdk coverage gain here (+5 statements, +2 branch arms, +1 function). The implicit-context lines themselves were already at hit count 60 on base.