Files
Aislo/resources/tester/test_b09_edits.py
T
eomsangdonandClaude Opus 5 9cf01ab15c feat(b09): 2차 편집 ① 자재단가대비표 채택 바꾸기 — 프로젝트 단위 고친 값(estimation.edits) · 서버가 얹어 다시 계산 · ↺
- B09_Estimation_Edits: 고친 값 한 벌 — 기본 조립(cached_build) 복사본에 얹음(캐시 키에 고친 값) · 기본 벌은 그대로(골든셋 무관)
  · 얹지 못한 값은 edit_skipped 로 남김 · 값 없는 슬롯은 받지 않음(422 + 까닭)
- 새 라우터 B09_Estimation_Router_Edits(GET/PUT /estimation/edits) — Router.py 는 _build_for 가 고친 값을 얹게만 바꿈
- 화면: 줄마다 채택 슬롯 고르개 · 일괄(변동없음/1~5 단가/최소단가) · 고친 줄 「사용자」 + ↺(지우면 계산값으로)
- 곁: 사용자 식 셈(B09_Estimation_Expression — ROUND·ROUNDDOWN·INT·SQRT, eval 없음)과 본표 편집 칸(UI_DetailEdit)을 먼저 둠 — 서버 편집 문이 서기 전까지 잠자 있음
- 검증: 시험 1656 통과 · ORCA 채택 저장 → 「사용자」·↺ → ↺ 뒤 고친 값 {} 로 복구 · 값 없는 슬롯 422 「경유: 1번 원천에 값이 없어」

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
2026-09-14 03:16:10 +09:00

66 lines
2.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""B09 사용자가 고친 값 — 프로젝트 단위 저장·얹기·되돌리기 (PLAN 12장 2차 · 2026-09-14 브레인 판정).
겨누는 것
① 고친 값은 **복사본에** 얹힘 — 기본 벌(캐시)은 그대로(골든셋·다른 프로젝트가 안 흔들림)
② 자재단가대비표 채택 슬롯 — 값 있는 슬롯만 받음 · 채택하면 금액이 그 슬롯 값으로 섬
③ 지우면(↺) 계산값으로 돌아감 · 고친 값이 없으면 캐시 키가 빈 글
"""
from __future__ import annotations
from decimal import Decimal
import pytest
from B09_Estimation.B09_Estimation_Edits import (
EditError,
apply_edits,
edits_key,
merge_changes,
)
from B09_Estimation.B09_Estimation_PriceBook import PriceBook, PriceDetail, PriceKind, PriceTitle
from B09_Estimation.B09_Estimation_UnitPrice import UnitPriceBuild
def _build() -> UnitPriceBuild:
book = PriceBook()
slots = [Decimal(100), None, Decimal(90), None, None, Decimal(120)]
book.add_title(PriceTitle("M-1", PriceKind.MATERIAL, "시멘트", slots=slots))
book.add_title(PriceTitle("B-1", PriceKind.UNIT_PRICE, "타설"))
book.add_detail(PriceDetail("B-1", "M-1", Decimal(2)))
return UnitPriceBuild(book=book)
def test_채택_슬롯을_바꾸면_복사본에서만_금액이_바뀐다() -> None:
base = _build()
edits = merge_changes({}, base, [{"section": "adopted_slots", "key": "M-1", "value": "3"}])
assert edits == {"adopted_slots": {"M-1": 3}}
edited = apply_edits(base, edits)
assert edited.book.resolve("B-1").material == Decimal(180) # 90 × 2
assert base.book.resolve("B-1").material == Decimal(240) # 기본 벌은 6번 120 그대로
assert edited.edit_skipped == []
def test_값_없는_슬롯은_받지_않는다() -> None:
with pytest.raises(EditError):
merge_changes({}, _build(), [{"section": "adopted_slots", "key": "M-1", "value": 2}])
with pytest.raises(EditError):
merge_changes({}, _build(), [{"section": "adopted_slots", "key": "B-1", "value": 1}])
def test_지우면_계산값으로_돌아가고_캐시_키가_빈다() -> None:
base = _build()
edits = merge_changes(
{"adopted_slots": {"M-1": 1}},
base,
[{"section": "adopted_slots", "key": "M-1", "value": None}],
)
assert edits == {} and edits_key(edits) == ""
assert edits_key({"adopted_slots": {"M-1": 1}}) != ""
def test_단가표에서_사라진_코드는_버리지_않고_남긴다() -> None:
edited = apply_edits(_build(), {"adopted_slots": {"M-없음": 1, "M-1": 2}})
assert len(edited.edit_skipped) == 2
assert edited.book.titles["M-1"].adopted_slot == 6