Merge remote-tracking branch 'origin/dev' into sub_laptop_8
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
"""343 덧 — 파생 저장본 도우미 · 유토 배분 저장본 · B07 읽기 안 지움 · 묶음 낡음 검사 기억."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
from pathlib import Path
|
||||
from uuid import uuid4
|
||||
|
||||
import pytest
|
||||
|
||||
from B06_Section import B06_Section_Router_HaulPlan as haul
|
||||
from common_util import common_util_derived_store as store
|
||||
from common_util import common_util_node_bundle as nb
|
||||
|
||||
# ── 도우미 ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def test_열쇠_같은_입력_같은_값() -> None:
|
||||
assert store.derived_key({"a": 1, "b": 2}, 3) == store.derived_key({"b": 2, "a": 1}, 3)
|
||||
assert store.derived_key({"a": 1}) != store.derived_key({"a": 2})
|
||||
|
||||
|
||||
def test_읽기_쓰기_null_도_맞음(tmp_path: Path) -> None:
|
||||
assert store.read_derived(tmp_path, "haul", "k") is None
|
||||
store.write_derived(tmp_path, "haul", "k", None)
|
||||
assert store.read_derived(tmp_path, "haul", "k") == {"key": "k", "value": None}
|
||||
assert (tmp_path / "_derived" / "haul" / "k.json").is_file()
|
||||
|
||||
|
||||
def test_종류마다_최근_두_벌_꺼내면_최근(tmp_path: Path) -> None:
|
||||
store.write_derived(tmp_path, "haul", "a", 1)
|
||||
time.sleep(0.02)
|
||||
store.write_derived(tmp_path, "haul", "b", 2)
|
||||
store.write_derived(tmp_path, "other", "x", 0)
|
||||
time.sleep(0.02)
|
||||
assert store.read_derived(tmp_path, "haul", "a")["value"] == 1
|
||||
time.sleep(0.02)
|
||||
store.write_derived(tmp_path, "haul", "c", 3)
|
||||
names = sorted(path.stem for path in (tmp_path / "_derived" / "haul").glob("*.json"))
|
||||
assert names == ["a", "c"]
|
||||
assert (tmp_path / "_derived" / "other" / "x.json").is_file()
|
||||
|
||||
|
||||
def test_결과_옆_열쇠와_파일_지문(tmp_path: Path) -> None:
|
||||
path = tmp_path / "m.key.json"
|
||||
assert not store.key_matches(path, {"a": 1})
|
||||
path.write_text(json.dumps({"a": 1}), encoding="utf-8")
|
||||
assert store.key_matches(path, {"a": 1}) and not store.key_matches(path, {"a": 2})
|
||||
assert store.file_stamp(tmp_path / "없음") is None and store.file_stamp(path)
|
||||
|
||||
|
||||
# ── 유토 배분 디스크 저장본 ─────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def _wire(monkeypatch: pytest.MonkeyPatch, root: Path, outputs: list) -> list:
|
||||
calls: list = []
|
||||
|
||||
async def _none(_project_id): # type: ignore[no-untyped-def]
|
||||
return {}
|
||||
|
||||
async def _stored(_fn, _project_id): # type: ignore[no-untyped-def]
|
||||
return "p"
|
||||
|
||||
def _run(_bundle, _script, payload): # type: ignore[no-untyped-def]
|
||||
calls.append(payload)
|
||||
return outputs.pop(0)
|
||||
|
||||
monkeypatch.setattr(haul, "haul_inputs_for", _none)
|
||||
monkeypatch.setattr(haul, "conversion_factors_for", _none)
|
||||
monkeypatch.setattr(haul, "haul_limits_for", _none)
|
||||
monkeypatch.setattr(haul, "_mass_haul_context", lambda *_: {"limits": []})
|
||||
monkeypatch.setattr(haul, "run_bundle_json", _run)
|
||||
monkeypatch.setattr(haul, "run_with_connection", _stored)
|
||||
monkeypatch.setattr(haul, "resolve_stored_project_path", lambda _p: str(root))
|
||||
haul._KEPT.clear()
|
||||
return calls
|
||||
|
||||
|
||||
def _post(result: dict) -> dict:
|
||||
response = asyncio.run(haul.compute_haul_plan(uuid4(), 7, {"result": result}))
|
||||
return json.loads(response.body)
|
||||
|
||||
|
||||
def test_유토_배분_재시작_뒤에도_셈_없음(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
calls = _wire(monkeypatch, tmp_path, [{"haul_plan": {"tag": "a"}}, {"haul_plan": {"tag": "b"}}])
|
||||
a = {"points": [{"cumulative_volume_m3": 1.0}]}
|
||||
assert _post(a)["haul_plan"] == {"tag": "a"} and len(calls) == 1
|
||||
key = haul._plan_key(a, {"limits": []}) # 화면 · 서버 메모리와 같은 열쇠
|
||||
assert (tmp_path / "_derived" / "haul" / f"{key}.json").is_file()
|
||||
haul._KEPT.clear() # 재시작
|
||||
assert _post(a)["haul_plan"] == {"tag": "a"} and len(calls) == 1
|
||||
b = {"points": [{"cumulative_volume_m3": 2.0}]}
|
||||
assert _post(b)["haul_plan"] == {"tag": "b"} and len(calls) == 2
|
||||
|
||||
|
||||
def test_유토_배분_저장소_못_찾아도_셈(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
calls = _wire(monkeypatch, tmp_path, [{"haul_plan": {"tag": "a"}}])
|
||||
|
||||
async def _missing(_fn, _project_id): # type: ignore[no-untyped-def]
|
||||
raise LookupError("없음")
|
||||
|
||||
monkeypatch.setattr(haul, "run_with_connection", _missing)
|
||||
assert _post({"points": [{"cumulative_volume_m3": 1.0}]})["haul_plan"] == {"tag": "a"}
|
||||
assert len(calls) == 1 and not (tmp_path / "_derived").exists()
|
||||
|
||||
|
||||
# ── B07 읽기는 측점 파일을 안 지움 ─────────────────────────────────────────────────
|
||||
|
||||
|
||||
def test_B07_읽기는_잔재를_걸러만_읽음(tmp_path: Path) -> None:
|
||||
from B07_DesignDetail.B07_DesignDetail_Router_Support_Io import _cross_files
|
||||
|
||||
cross_dir = tmp_path / "B05_Profile" / "cross_sections"
|
||||
cross_dir.mkdir(parents=True)
|
||||
for name in ("cross_00000m.json", "cross_00020m.json", "cross_00040m.json"):
|
||||
(cross_dir / name).write_text("{}", encoding="utf-8")
|
||||
longitudinal_path = tmp_path / "B05_Profile" / "longitudinal" / "longitudinal.json"
|
||||
files = _cross_files(longitudinal_path, {"stations": [{"chainage_m": 0}, {"chainage_m": 20}]})
|
||||
assert [path.name for path in files] == ["cross_00000m.json", "cross_00020m.json"]
|
||||
assert (cross_dir / "cross_00040m.json").is_file()
|
||||
|
||||
|
||||
# ── 묶음 낡음 검사 기억 ─────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def bundle(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
|
||||
monkeypatch.setattr(nb, "ROOT", tmp_path)
|
||||
monkeypatch.setattr(nb, "_FRESH", {})
|
||||
path = tmp_path / "dist" / "bundle.mjs"
|
||||
path.parent.mkdir()
|
||||
path.write_text("", encoding="utf-8")
|
||||
os.utime(path, (1_000_000_000, 1_000_000_000))
|
||||
return path
|
||||
|
||||
|
||||
def _counting(monkeypatch: pytest.MonkeyPatch) -> list:
|
||||
scans: list = []
|
||||
real = nb._sources_newer
|
||||
|
||||
def counting(built_at: float) -> bool:
|
||||
scans.append(built_at)
|
||||
return real(built_at)
|
||||
|
||||
monkeypatch.setattr(nb, "_sources_newer", counting)
|
||||
return scans
|
||||
|
||||
|
||||
def test_낡지_않음은_잠시_기억(bundle: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
scans = _counting(monkeypatch)
|
||||
assert not nb.bundle_stale(bundle) and not nb.bundle_stale(bundle)
|
||||
assert len(scans) == 1
|
||||
# 묶음을 새로 지으면(mtime 바뀜) 다시 잼
|
||||
os.utime(bundle, (1_000_000_100, 1_000_000_100))
|
||||
assert not nb.bundle_stale(bundle) and len(scans) == 2
|
||||
# 기억 시간이 지나면 다시 잼
|
||||
monkeypatch.setattr(nb, "RECHECK_S", 0.0)
|
||||
assert not nb.bundle_stale(bundle) and len(scans) == 3
|
||||
|
||||
|
||||
def test_낡음은_기억하지_않음(bundle: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
source = nb.ROOT / "B05_Profile" / "x.ts"
|
||||
source.parent.mkdir()
|
||||
source.write_text("", encoding="utf-8")
|
||||
scans = _counting(monkeypatch)
|
||||
assert nb.bundle_stale(bundle) and nb.bundle_stale(bundle)
|
||||
assert len(scans) == 2 and str(bundle) not in nb._FRESH
|
||||
@@ -0,0 +1,128 @@
|
||||
"""346-4 되돌리기 앞일 — 사진 대상 = aislo:draft: 전부 · 누름 중간은 안 찍고 놓을 때 한 번 · 크기.
|
||||
|
||||
./venv/Scripts/python.exe -m pytest resources/tester/test_346_4_profile_history.py -q
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
TS_FILE = ROOT / "B05_Profile" / "B05_Profile_UI_Profile_History.ts"
|
||||
TSC = ROOT / "config" / "node_modules" / "typescript" / "bin" / "tsc"
|
||||
|
||||
DRIVER = r"""
|
||||
import { writeFileSync } from "node:fs";
|
||||
// 세션 흉내 — getItem 은 진짜처럼 매번 새 글을 돌려줌(사진끼리 글을 같이 쓰는지 재려고).
|
||||
const data = new Map();
|
||||
globalThis.sessionStorage = {
|
||||
get length() { return data.size; },
|
||||
key: (i) => [...data.keys()][i] ?? null,
|
||||
getItem: (k) => (data.has(k) ? Buffer.from(data.get(k)).toString() : null),
|
||||
setItem: (k, v) => data.set(k, String(v)),
|
||||
removeItem: (k) => data.delete(k),
|
||||
};
|
||||
globalThis.window = new EventTarget();
|
||||
window.setTimeout = setTimeout;
|
||||
const { createProfileHistory } = await import("./B05_Profile_UI_Profile_History.js");
|
||||
const tick = () => new Promise((r) => setTimeout(r, 5));
|
||||
const out = {};
|
||||
|
||||
// 1. 대상 — 초안 통 전부 + 옛 키 · 보기 설정 · pref · result 는 뺌
|
||||
const K = {
|
||||
route: "aislo:draft:route-draft:p1",
|
||||
cross: "aislo:draft:crossdesign:p1:7",
|
||||
pipes: "aislo:draft:pipes:p1",
|
||||
legacy: "b05:uphill:p1",
|
||||
layout: "b05-panel-collapsed",
|
||||
pref: "aislo:pref:table-open",
|
||||
result: "aislo:result:latest:p1",
|
||||
};
|
||||
for (const k of Object.values(K)) sessionStorage.setItem(k, "0");
|
||||
let restored = 0;
|
||||
const h = createProfileHistory(() => (restored += 1));
|
||||
for (const k of Object.values(K)) sessionStorage.setItem(k, "1");
|
||||
h.record();
|
||||
out.undo = h.undo();
|
||||
out.after = Object.fromEntries(Object.entries(K).map(([n, k]) => [n, sessionStorage.getItem(k)]));
|
||||
out.restored = restored;
|
||||
|
||||
// 2. 누르고 있는 동안 기록 미룸 · 놓으면 한 번
|
||||
data.clear();
|
||||
sessionStorage.setItem(K.cross, "a");
|
||||
const h2 = createProfileHistory(() => {});
|
||||
window.dispatchEvent(new Event("pointerdown"));
|
||||
for (const v of ["b", "c", "d", "e"]) {
|
||||
sessionStorage.setItem(K.cross, v);
|
||||
h2.record();
|
||||
}
|
||||
out.heldCanUndo = h2.canUndo();
|
||||
window.dispatchEvent(new Event("pointerup"));
|
||||
await tick();
|
||||
out.releasedCanUndo = h2.canUndo();
|
||||
h2.undo();
|
||||
out.oneUndo = sessionStorage.getItem(K.cross);
|
||||
out.noMoreUndo = !h2.canUndo();
|
||||
|
||||
// 3. 크기 — 300 KB 칸 셋 · 작은 칸만 바꾸며 100단
|
||||
data.clear();
|
||||
const big = (c) => c.repeat(300 * 1024);
|
||||
sessionStorage.setItem(K.route, big("r"));
|
||||
sessionStorage.setItem(K.cross, big("x"));
|
||||
sessionStorage.setItem(K.pipes, big("p"));
|
||||
globalThis.gc();
|
||||
const before = process.memoryUsage().heapUsed;
|
||||
const h3 = createProfileHistory(() => {});
|
||||
for (let i = 0; i < 100; i += 1) {
|
||||
sessionStorage.setItem("aislo:draft:alignment-draft:p1:7", JSON.stringify({ i }));
|
||||
h3.record();
|
||||
}
|
||||
globalThis.gc();
|
||||
out.heapMb = (process.memoryUsage().heapUsed - before) / 1024 / 1024;
|
||||
let steps = 0;
|
||||
while (h3.undo()) steps += 1;
|
||||
out.steps = steps;
|
||||
writeFileSync(process.argv[2], JSON.stringify(out));
|
||||
"""
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def result(tmp_path_factory: pytest.TempPathFactory) -> dict:
|
||||
out = tmp_path_factory.mktemp("ts_history")
|
||||
subprocess.run( # noqa: S603 — 고정 실행 파일
|
||||
["node", str(TSC), str(TS_FILE), "--outDir", str(out), "--module", "esnext",
|
||||
"--target", "es2022", "--moduleResolution", "bundler", "--ignoreConfig", "--noCheck",
|
||||
"--noResolve"],
|
||||
cwd=str(ROOT), check=True, capture_output=True,
|
||||
) # fmt: skip
|
||||
(out / "driver.mjs").write_text(DRIVER, encoding="utf-8")
|
||||
run = subprocess.run( # noqa: S603
|
||||
["node", "--expose-gc", str(out / "driver.mjs"), str(out / "out.json")],
|
||||
capture_output=True, text=True, cwd=str(out),
|
||||
) # fmt: skip
|
||||
assert run.returncode == 0, run.stderr
|
||||
return json.loads((out / "out.json").read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
def test_초안_통_전부를_되돌림(result) -> None:
|
||||
after = result["after"]
|
||||
assert result["undo"] and result["restored"] == 1
|
||||
assert after["route"] == after["cross"] == after["pipes"] == after["legacy"] == "0"
|
||||
# 보기 설정 · pref · result 는 사진 밖 — 되돌리기가 안 건드림
|
||||
assert after["layout"] == after["pref"] == after["result"] == "1"
|
||||
|
||||
|
||||
def test_누름_중간은_안_찍고_놓을_때_한_번(result) -> None:
|
||||
assert result["heldCanUndo"] is False
|
||||
assert result["releasedCanUndo"] is True
|
||||
assert result["oneUndo"] == "a" and result["noMoreUndo"]
|
||||
|
||||
|
||||
def test_큰_칸_100단도_바뀐_칸만_늚(result) -> None:
|
||||
assert result["steps"] == 99 # 한도 100(첫 사진 포함)
|
||||
# 같은 글을 같이 쓰지 않으면 900 KB × 100 ≈ 90 MB(UTF-8 1바이트 글 기준 · 실제는 더)
|
||||
assert result["heapMb"] < 10, result["heapMb"]
|
||||
@@ -0,0 +1,169 @@
|
||||
"""347-2 「지금 노선으로 셈」 서버 길 · 347-3 관 자리 후보.
|
||||
|
||||
- 늦은 차례: 계산 중 새 차례가 오면 단계 사이에서 그만둠(superseded) · 작업본에 안 씀.
|
||||
- 메모리 격자로 세부 유역(파일 안 읽음) · 노선 초안 → 그린 노선.
|
||||
- 관 자리 후보: 최대 간격(100 m)을 넘는 구간에만 · 기존 관 자리엔 안 냄 · 관 목록엔 안 넣음.
|
||||
- [확정](recompute) = 낡았으면 배수유역부터 다시 셈 · [저장] = 안 셈.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import math
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from uuid import uuid4
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
from shapely.geometry import LineString
|
||||
|
||||
from B04_PreProcess import B04_PreProcess_Engine_Watershed_Expand as expand
|
||||
from B04_PreProcess import B04_PreProcess_Router_Basins as basins
|
||||
from B04_PreProcess import B04_PreProcess_Router_Watershed_Live as live
|
||||
from B04_PreProcess.B04_PreProcess_Engine_Watershed_Grid import GridSpec
|
||||
from common_util.common_util_drainage_context import DrainageContext
|
||||
from common_util.common_util_drainage_detail import (
|
||||
RoadRouting,
|
||||
build_detail,
|
||||
pipes_from_chainages,
|
||||
suggest_pipe_positions,
|
||||
)
|
||||
from common_util.common_util_route_geometry import RouteVertex
|
||||
|
||||
VERTICES = [RouteVertex(0.0, 0.0, 100.0, 0.0), RouteVertex(300.0, 0.0, 90.0, 300.0)]
|
||||
|
||||
|
||||
def test_draft_vertices_follow_nodes():
|
||||
draft = {"nodes": [[0, 0], [100, 0], [100, 100]], "curveOn": [False, False, False]}
|
||||
vertices = live.draft_vertices(draft, 30.0)
|
||||
assert vertices[0].x == 0 and vertices[-1].y == 100
|
||||
assert math.isclose(vertices[-1].chainage_m, 200.0)
|
||||
|
||||
|
||||
def test_candidates_only_in_long_gaps():
|
||||
strength = np.zeros(301)
|
||||
strength[150] = 50.0 # 흐름이 모이는 자리
|
||||
pipes = pipes_from_chainages(VERTICES, [0.0, 300.0])
|
||||
picked = suggest_pipe_positions(VERTICES, pipes, strength)
|
||||
assert picked and all(0 < pipe.chainage_m < 300 for pipe in picked)
|
||||
assert all(pipe.chainage_m == round(pipe.chainage_m) for pipe in picked) # 정수 m
|
||||
dense = pipes_from_chainages(VERTICES, [0.0, 90.0, 180.0, 270.0, 300.0])
|
||||
assert suggest_pipe_positions(VERTICES, dense, strength) == []
|
||||
|
||||
|
||||
def _routing() -> RoadRouting:
|
||||
spec = GridSpec(x_min=-5.0, y_max=5.0, cell_m=1.0, n_rows=10, n_cols=310)
|
||||
road_index = np.arange(5 * 310 + 5, 5 * 310 + 305, dtype=np.int32)
|
||||
road_slot = np.full(spec.size, -1, dtype=np.int32)
|
||||
road_slot[road_index] = np.arange(road_index.size)
|
||||
return RoadRouting(
|
||||
spec=spec,
|
||||
road_slot=road_slot,
|
||||
path_length=np.zeros(spec.size, np.float32),
|
||||
elevation=np.full(spec.size, 95.0, np.float32),
|
||||
road_cell_index=road_index,
|
||||
road_chainage=np.arange(road_index.size, dtype=np.float64),
|
||||
strength=np.ones(road_index.size, np.int64),
|
||||
)
|
||||
|
||||
|
||||
def test_build_detail_uses_memory_routing(tmp_path):
|
||||
missing = tmp_path / "없는 폴더" # 파일을 읽으면 None 이 됨
|
||||
detail = build_detail(missing, VERTICES, [0.0, 300.0], _routing(), [])
|
||||
assert detail is not None and detail.pipe_candidates
|
||||
assert build_detail(missing, VERTICES, [0.0, 300.0]) is None
|
||||
|
||||
|
||||
def _patch_live(monkeypatch, preview_fn):
|
||||
async def prepare(project_id):
|
||||
return {"contours": [], "streams": [], "contour_key": "k"}
|
||||
|
||||
async def load(project_id):
|
||||
return DrainageContext(stored_path="x", project_root=Path("."), vertices=VERTICES), ""
|
||||
|
||||
monkeypatch.setattr(live, "_prepare", prepare)
|
||||
monkeypatch.setattr(live, "load_drainage_context", load)
|
||||
monkeypatch.setattr(live, "drainage_dir", lambda stored: Path("."))
|
||||
monkeypatch.setattr(live, "preview_stages", preview_fn)
|
||||
calls = SimpleNamespace(build=0)
|
||||
|
||||
def build(*args):
|
||||
calls.build += 1
|
||||
return None, []
|
||||
|
||||
monkeypatch.setattr(live, "_build", build)
|
||||
return calls
|
||||
|
||||
|
||||
def test_newer_generation_stops_between_stages(monkeypatch):
|
||||
pid = uuid4()
|
||||
|
||||
def preview(vertices, contours, streams, store, should_stop):
|
||||
live._LATEST_GENERATION[str(pid)] = 8 # 계산 중 새 조작 → 새 요청이 차례를 올림
|
||||
if should_stop():
|
||||
raise expand.AnalysisStopped
|
||||
|
||||
calls = _patch_live(monkeypatch, preview)
|
||||
result = asyncio.run(live.post_drainage_live(pid, {"generation": 7, "points": []}))
|
||||
assert result == live.SUPERSEDED and calls.build == 0
|
||||
|
||||
|
||||
def test_newer_generation_after_preview_skips_split(monkeypatch):
|
||||
pid = uuid4()
|
||||
|
||||
def preview(vertices, contours, streams, store, should_stop):
|
||||
assert not should_stop()
|
||||
live._LATEST_GENERATION[str(pid)] = 3
|
||||
return SimpleNamespace(routing=object())
|
||||
|
||||
calls = _patch_live(monkeypatch, preview)
|
||||
result = asyncio.run(live.post_drainage_live(pid, {"generation": 2}))
|
||||
assert result == live.SUPERSEDED and calls.build == 0
|
||||
|
||||
|
||||
def test_engine_stops_in_expansion_round():
|
||||
contours = [
|
||||
{
|
||||
"geometry": {
|
||||
"type": "LineString",
|
||||
"coordinates": [
|
||||
[150 + r * math.cos(t), -150 + r * math.sin(t)]
|
||||
for t in np.linspace(0, 2 * math.pi, 121)
|
||||
],
|
||||
},
|
||||
"properties": {"elevation": 300 - r},
|
||||
}
|
||||
for r in range(10, 200, 10)
|
||||
]
|
||||
spec = GridSpec(x_min=100.0, y_max=-100.0, cell_m=1.0, n_rows=100, n_cols=100)
|
||||
domain = np.zeros((100, 100), bool)
|
||||
domain[20:80, 20:80] = True
|
||||
route = LineString([(110.0, -150.0), (190.0, -150.0)])
|
||||
with pytest.raises(expand.AnalysisStopped):
|
||||
expand.expand_by_red_boundary(
|
||||
spec, domain, contours, route, [], None, should_stop=lambda: True
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"recompute, stale, expected", [(True, True, 1), (True, False, 0), (False, True, 0)]
|
||||
)
|
||||
def test_confirm_recomputes_only_when_stale(monkeypatch, recompute, stale, expected):
|
||||
import B04_PreProcess.B04_PreProcess_Router_Watershed as watershed
|
||||
|
||||
calls = []
|
||||
|
||||
async def is_stale(project_id):
|
||||
return stale
|
||||
|
||||
async def region(project_id, refresh=False):
|
||||
calls.append(refresh)
|
||||
return {"status": "success"}
|
||||
|
||||
async def resolve(*args, **kwargs):
|
||||
return basins.JSONResponse(status_code=404, content={})
|
||||
|
||||
monkeypatch.setattr(basins, "_analysis_stale", is_stale)
|
||||
monkeypatch.setattr(watershed, "get_primary_region", region)
|
||||
monkeypatch.setattr(basins, "_resolve", resolve)
|
||||
asyncio.run(basins.put_pipe_points(uuid4(), {"points": []}, None, recompute))
|
||||
assert len(calls) == expected and all(calls)
|
||||
Reference in New Issue
Block a user