- `resources/data_master_sources/sources_2026-09-15.json` — 갈래·자료 이름·내는 곳·받는 자리· 주기·최신 공표일·우리 판·확인한 날·비고. **값은 안 담음** - ⭐ **건설 노임이 뒤처진 것으로 섬** — 우리 2026-01-01 · 최신 2026-09-01 · 주기가 1.1 / 9.1 임(7.1 로 알던 것이 틀렸음) · 받는 자리 cak.or.kr [지원·사업]>[건설적산기준]>[건설임금] - **우리 판은 실제 파일에서 읽음** — 손으로 적으면 갱신하고 안 고쳐 또 뒤처짐. 파생본은 `derived_from.effective_date` 를 따라감(운전경비 = 2026-01-01 · 「판 모름」 아님) - 받는 자리·최신 공표일을 **모르는 칸은 비우고 사유를 닮** — 주소를 지어내지 않음(빈 칸 다섯) - 셈 — 뒤처짐 1 · 최신 3 · 모름 4(수시·일별이라 한 날로 안 정해지는 자재·유가 셋 포함) - 새 시험 `test_master_sources.py` 11건 — 표의 우리 판이 실제 파일과 어긋나거나 뒤처짐 판정이 뒤집히거나 모르는 칸에 사유가 없으면 빨강 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FFCnEYNH4tsS2MbHvzBhZk
129 lines
5.2 KiB
Python
129 lines
5.2 KiB
Python
"""자료 출처표(`resources/data_master_sources/sources_2026-09-15.json`) 시험.
|
|
|
|
이 표가 있는 까닭 — 마스터 파일 안에는 **제 판만** 있고 「최신이 무엇인지·어디서 받는지」가
|
|
없어서 건설 노임이 반 년 뒤처진 것을 아무도 몰랐다(2026-09-15).
|
|
|
|
- `our_edition` 이 **실제 파일과 어긋나면 빨강** — 손으로 적힌 판이 굳는 것을 막는다
|
|
- 뒤처짐 판정이 뒤집히면 빨강
|
|
- 받는 자리를 모르는 줄에 **사유가 없으면 빨강**(주소를 지어내지 않기로 한 자리)
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
SOURCES_PATH = ROOT / "resources" / "data_master_sources" / "sources_2026-09-15.json"
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def sources() -> dict:
|
|
return json.loads(SOURCES_PATH.read_text(encoding="utf-8"))
|
|
|
|
|
|
def _edition_of(path: Path) -> tuple[str, str]:
|
|
doc = json.loads(path.read_text(encoding="utf-8"))
|
|
if doc.get("effective_date"):
|
|
return doc["effective_date"], "effective_date"
|
|
derived = doc.get("derived_from")
|
|
if isinstance(derived, dict) and derived.get("effective_date"):
|
|
return derived["effective_date"], "derived_from.effective_date"
|
|
return "", ""
|
|
|
|
|
|
def test_출처표가_읽힌다(sources):
|
|
assert sources["dataset_id"] == "data_master_sources"
|
|
assert sources["sources"], "출처가 하나도 없다"
|
|
|
|
|
|
def test_가리키는_파일이_다_있다(sources):
|
|
missing = [
|
|
path for row in sources["sources"] for path in row["files"] if not (ROOT / path).is_file()
|
|
]
|
|
assert not missing, f"없는 파일을 가리킨다: {missing}"
|
|
|
|
|
|
def test_우리_판이_실제_파일과_같다(sources):
|
|
"""⭐ 손으로 적힌 판이 굳으면 또 뒤처진다 — 파일에서 읽은 값과 대 본다."""
|
|
wrong = []
|
|
for row in sources["sources"]:
|
|
edition, where = _edition_of(ROOT / row["files"][0])
|
|
if row["our_edition"] != edition:
|
|
wrong.append(f"{row['source_id']}: 표 {row['our_edition']} ≠ 파일 {edition}")
|
|
elif row["our_edition_from"] != where:
|
|
wrong.append(f"{row['source_id']}: 읽은 자리 {row['our_edition_from']} ≠ {where}")
|
|
assert not wrong, wrong
|
|
|
|
|
|
def test_뒤처짐_판정이_맞다(sources):
|
|
for row in sources["sources"]:
|
|
latest, ours = row["latest_published"], row["our_edition"]
|
|
if not latest or not ours:
|
|
assert row["status"] == "unknown", row["source_id"]
|
|
elif latest > ours:
|
|
assert row["status"] == "behind", row["source_id"]
|
|
else:
|
|
assert row["status"] == "current", row["source_id"]
|
|
|
|
|
|
def test_건설_노임이_뒤처진_것으로_선다(sources):
|
|
"""2026-09-15 에 드러난 자리 — 우리 2026-01-01, 최신 2026-09-01."""
|
|
row = next(r for r in sources["sources"] if r["source_id"] == "labor_const")
|
|
assert row["status"] == "behind"
|
|
assert row["our_edition"] == "2026-01-01"
|
|
assert row["latest_published"] == "2026-09-01"
|
|
# 주기를 7월 1일로 알던 것이 틀렸다 — 1.1 / 9.1 이다.
|
|
assert "9월 1일" in row["cycle"]
|
|
assert row["where_to_get"], "받는 자리를 받았는데 비어 있다"
|
|
|
|
|
|
def test_운전경비는_판_모름이_아니다(sources):
|
|
"""파일 안 `derived_from.effective_date` 에 판이 박혀 있다 — 「모름」으로 적으면 안 된다."""
|
|
row = next(r for r in sources["sources"] if r["source_id"] == "machine_operating")
|
|
assert row["our_edition"] == "2026-01-01"
|
|
assert row["our_edition_from"] == "derived_from.effective_date"
|
|
assert row["status"] != "unknown"
|
|
|
|
|
|
def test_모르는_칸에는_사유가_있다(sources):
|
|
"""주소·최신 공표일을 지어내지 않기로 한 자리 — 빈 칸이면 사유가 있어야 한다."""
|
|
bad = []
|
|
for row in sources["sources"]:
|
|
if not row["where_to_get"] and not row.get("where_to_get_missing_reason"):
|
|
bad.append(f"{row['source_id']} — 받는 자리 사유 없음")
|
|
if not row["latest_published"] and not row.get("latest_missing_reason"):
|
|
bad.append(f"{row['source_id']} — 최신 공표일 사유 없음")
|
|
assert not bad, bad
|
|
|
|
|
|
def test_줄마다_갈래와_주기가_있다(sources):
|
|
known = set(sources["kinds"])
|
|
for row in sources["sources"]:
|
|
assert row["kind"] in known, row["source_id"]
|
|
assert row["kind_ko"] == sources["kinds"][row["kind"]]
|
|
assert row["cycle"].strip(), row["source_id"]
|
|
assert row["publisher"].strip(), row["source_id"]
|
|
assert row["checked_on"].strip(), row["source_id"]
|
|
|
|
|
|
def test_기초단가_다섯_갈래를_다_덮는다(sources):
|
|
covered = {row["kind"] for row in sources["sources"]}
|
|
assert covered == {"labor", "machine", "material", "oil", "rate"}
|
|
|
|
|
|
def test_세어_둔_수가_실제와_같다(sources):
|
|
counts = sources["counts"]
|
|
assert counts["sources"] == len(sources["sources"])
|
|
tallied: dict[str, int] = {}
|
|
for row in sources["sources"]:
|
|
tallied[row["status"]] = tallied.get(row["status"], 0) + 1
|
|
assert counts["by_status"] == tallied
|
|
|
|
|
|
def test_상태_낱말이_다_풀려_있다(sources):
|
|
known = set(sources["status"])
|
|
assert {row["status"] for row in sources["sources"]} <= known
|