Skip to content

Commit 899e93b

Browse files
committed
test(native): replay macro-returned reference call errors for every consumer
1 parent 78a6706 commit 899e93b

3 files changed

Lines changed: 150 additions & 0 deletions

File tree

‎tests/e2e/src/sqlbuild/cli/commands/main/compile/_test_types.py‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -889,6 +889,15 @@ class RejectedReferenceCallTestCase:
889889
expected_diagnostics: tuple[tuple[str, str, str, int, int, str], ...]
890890

891891

892+
@dataclass(frozen=True)
893+
class MacroReferenceCallStoreTestCase:
894+
"""Repeated compiles of models sharing a macro that returns a rejected reference call."""
895+
896+
description: str
897+
engine: str
898+
expected_logged_calls: tuple[int, ...]
899+
900+
892901
@dataclass(frozen=True)
893902
class MacroCallStoreEditStep:
894903
"""One edit compiled with the macro call store, and the executions it expects."""

‎tests/e2e/src/sqlbuild/cli/commands/main/compile/helpers.py‎

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4274,6 +4274,96 @@ def prepare_reference_call_project(*, tmp_path: Path, staging_from: str, mart_fr
42744274
return project_dir
42754275

42764276

4277+
_MACRO_REFERENCE_CALL_MODELS: tuple[str, ...] = ("customer_orders", "customer_returns")
4278+
_DIAGNOSTIC_FIELDS: tuple[str, ...] = (
4279+
"code",
4280+
"message",
4281+
"resource_name",
4282+
"path",
4283+
"line",
4284+
"column",
4285+
"help",
4286+
)
4287+
4288+
4289+
class MacroReferenceCallRuns(NamedTuple):
4290+
"""Repeated compiles: exit codes, macro executions, and the diagnostics of each run."""
4291+
4292+
returncodes: tuple[int, ...]
4293+
logged_calls: tuple[int, ...]
4294+
diagnostics: tuple[tuple[tuple[object, ...], ...], ...]
4295+
4296+
4297+
def prepare_macro_reference_call_project(*, tmp_path: Path) -> Path:
4298+
"""Write a project whose models share a macro returning a reference call compile rejects."""
4299+
4300+
return prepare_inline_project(
4301+
tmp_path=tmp_path,
4302+
project_name="orders",
4303+
repo_files={
4304+
"sqlbuild_project.toml": _REFERENCE_CALL_PROJECT_TOML,
4305+
"sources/raw.yml": _REFERENCE_CALL_SOURCES_YML,
4306+
"models/staging/customers.sql": (
4307+
'MODEL (description "Customers.", materialized view);\n\n'
4308+
'SELECT order_id AS customer_id FROM __source("raw_orders")\n'
4309+
),
4310+
"models/marts/_sqlbuild/_macros/customer_relation.py": (
4311+
'"""A macro that returns an unquoted reference call."""\n\n'
4312+
"import os\n\n\n"
4313+
"def customer_relation() -> str:\n"
4314+
' """Return the customers relation, logging each execution."""\n'
4315+
f' path = os.environ.get("{MACRO_CALL_LOG_ENV_VAR}")\n'
4316+
" if path:\n"
4317+
' with open(path, "a", encoding="utf-8") as log:\n'
4318+
' log.write("customer_relation\\n")\n'
4319+
' return "__ref(customers)"\n'
4320+
),
4321+
**{
4322+
f"models/marts/{name}.sql": (
4323+
f'MODEL (description "{name}.", materialized view);\n\n'
4324+
"SELECT customer_id FROM @customer_relation()\n"
4325+
)
4326+
for name in _MACRO_REFERENCE_CALL_MODELS
4327+
},
4328+
},
4329+
)
4330+
4331+
4332+
def _diagnostic_fields(item: dict[str, object]) -> tuple[object, ...]:
4333+
return tuple(item.get(field) for field in _DIAGNOSTIC_FIELDS)
4334+
4335+
4336+
def macro_reference_call_runs(
4337+
*, project_dir: Path, log_path: Path, engine: str, runs: int
4338+
) -> MacroReferenceCallRuns:
4339+
"""Compile `runs` times in fresh processes, keeping the macro call store between runs."""
4340+
4341+
env: dict[str, str] = {
4342+
COMPILER_ENGINE_ENV_VAR: engine,
4343+
REUSE_DISABLE_ENV_VAR: "1",
4344+
MACRO_CALL_LOG_ENV_VAR: str(log_path),
4345+
}
4346+
returncodes: list[int] = []
4347+
logged_calls: list[int] = []
4348+
diagnostics: list[tuple[tuple[object, ...], ...]] = []
4349+
for _ in range(runs):
4350+
_ = log_path.write_text("", encoding="utf-8")
4351+
compiled: subprocess.CompletedProcess[str] = run_installed_sqb(
4352+
project_dir=project_dir, args=("compile", "--json"), env=env
4353+
)
4354+
returncodes.append(compiled.returncode)
4355+
logged_calls.append(len(log_path.read_text(encoding="utf-8").splitlines()))
4356+
items: list[dict[str, object]] = cast(
4357+
list[dict[str, object]], json.loads(compiled.stdout)["diagnostics"]
4358+
)
4359+
diagnostics.append(tuple(_diagnostic_fields(item) for item in items))
4360+
return MacroReferenceCallRuns(
4361+
returncodes=tuple(returncodes),
4362+
logged_calls=tuple(logged_calls),
4363+
diagnostics=tuple(diagnostics),
4364+
)
4365+
4366+
42774367
MACRO_CALL_STORE_ENGINE: str = "native-preview"
42784368
STORE_ENVIRONMENT_REGION_VAR: str = "SQB_STORE_TEST_REGION"
42794369
STORE_ARGUMENT_ENV_VAR: str = "STORE_TEST_ARGUMENT"

‎tests/e2e/src/sqlbuild/cli/commands/main/compile/test_compile_macro_call_store.py‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
EngineMacroCallGateTestCase,
1313
MacroCallStoreEditSequenceTestCase,
1414
MacroCallStoreEditStep,
15+
MacroReferenceCallStoreTestCase,
1516
SecondCompileStoreTestCase,
1617
StaleMacroModuleStoreTestCase,
1718
StaleStoreArrangement,
@@ -23,6 +24,7 @@
2324
CompileReuseRun,
2425
EngineMacroCallRuns,
2526
MacroCallStoreRun,
27+
MacroReferenceCallRuns,
2628
backdate_flavor_between_processes,
2729
block_proc_reads,
2830
compile_as_new_process_here,
@@ -36,8 +38,10 @@
3638
in_process_reuse_run,
3739
logged_in_process_compile,
3840
macro_call_store_compile,
41+
macro_reference_call_runs,
3942
move_flavor_while_saving,
4043
prepare_macro_call_store_project,
44+
prepare_macro_reference_call_project,
4145
pretend_fresh_process,
4246
recompile_in_process_after_edit,
4347
replace_project_text,
@@ -50,6 +54,11 @@
5054
)
5155
from tests.integration.src.sqlbuild.compiler.compile.helpers import MACRO_CALL_LOG_ENV_VAR
5256

57+
_MACRO_REFERENCE_CALL_RUNS: int = 2
58+
_MACRO_REFERENCE_CALL_MESSAGE: str = (
59+
"__ref(customers) is not a valid __ref() call, returned by macro customer_relation()"
60+
)
61+
_MACRO_REFERENCE_CALL_PATH: str = "models/marts/_sqlbuild/_macros/customer_relation.py"
5362
_EDIT_STEPS: tuple[MacroCallStoreEditStep, ...] = (
5463
MacroCallStoreEditStep(
5564
description="no_change",
@@ -305,6 +314,48 @@ def test_given_engine_when_compiling_repeatedly_then_only_preview_batches_and_st
305314
assert runs.store_files == test_case.expected_store_files
306315

307316

317+
@pytest.mark.parametrize(
318+
"test_case",
319+
[
320+
MacroReferenceCallStoreTestCase(
321+
description="python",
322+
engine="python",
323+
expected_logged_calls=(2, 2),
324+
),
325+
MacroReferenceCallStoreTestCase(
326+
description="native_preview_memo_then_store",
327+
engine="native-preview",
328+
expected_logged_calls=(1, 0),
329+
),
330+
],
331+
ids=lambda case: case.description,
332+
)
333+
def test_given_macro_returning_rejected_reference_call_when_replaying_then_reports_match_python(
334+
tmp_path: Path, test_case: MacroReferenceCallStoreTestCase
335+
) -> None:
336+
oracle: MacroReferenceCallRuns = macro_reference_call_runs(
337+
project_dir=prepare_macro_reference_call_project(tmp_path=tmp_path / "python"),
338+
log_path=tmp_path / "python.log",
339+
engine="python",
340+
runs=1,
341+
)
342+
343+
runs: MacroReferenceCallRuns = macro_reference_call_runs(
344+
project_dir=prepare_macro_reference_call_project(tmp_path=tmp_path / "engine"),
345+
log_path=tmp_path / "engine.log",
346+
engine=test_case.engine,
347+
runs=_MACRO_REFERENCE_CALL_RUNS,
348+
)
349+
350+
assert [diagnostic[:4] for diagnostic in oracle.diagnostics[0]] == [
351+
("P012", _MACRO_REFERENCE_CALL_MESSAGE, name, _MACRO_REFERENCE_CALL_PATH)
352+
for name in ("customer_orders", "customer_returns")
353+
]
354+
assert runs.returncodes == (1,) * _MACRO_REFERENCE_CALL_RUNS
355+
assert runs.logged_calls == test_case.expected_logged_calls
356+
assert runs.diagnostics == oracle.diagnostics * _MACRO_REFERENCE_CALL_RUNS
357+
358+
308359
@pytest.mark.parametrize(
309360
"test_case",
310361
[

0 commit comments

Comments
 (0)