feat(532-3): E02 지반 조건 — 기본값 정본 · 물성 표(지층 추가 · 지움) · 구간 덮어쓰기 · KDS 기준안전율 · 노면 하중 · 지하수 · 지진 · 저장 · 초기화
- 기본값 정본 resources/master_template/stability/ground_defaults.json — 지층 여섯 · 값마다 출처(07 표 · 절) · KDS 11 70 05 표 4.3-1~3 · 노면 10 kPa · 지하수 규칙 · 지진 끔 · 범위
- 07 원문 대조 — 성토 φ 25 → 28(07 §4 종합 28~30 아래값) · 나머지 범위 안
- API GET · PUT /api/projects/{id}/stab/ground · POST …/reset — 작업본 E02_StabGround/ground.json · 초기값 initial_snapshot/E02_StabGround/ground.json(E 생성 직후 기본값 + B06 측점 암 경계) · 측점은 서버 값
- 검사 · 측점 적용 = 파이썬 · TS 짝(E02_StabGround_Model.py ↔ logic/E02_StabGround_Check.ts) · 거울 시험
- 화면 — 카드 넷 · 칸 위 근거 · 기본값 툴팁 · 틀린 칸 표시 · sessionStorage 캐시 · 자동저장 없음
- test_528 E01 시험 — 생성 뒤 E02 초기값 부름 확인 더함
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"version": 1,
|
||||
"note": "문헌 추정치 · 현장 시험 없음 · 값은 사용자가 바꿀 수 있음(E02 지반 조건)",
|
||||
"materials": [
|
||||
{ "id": "weathered_soil", "name": "풍화토", "gamma_t": 18.0, "gamma_sat": 19.0, "c": 15.0, "phi": 30.0, "water": "soil" },
|
||||
{ "id": "weathered_rock", "name": "풍화암", "gamma_t": 20.0, "gamma_sat": 21.0, "c": 30.0, "phi": 33.0, "water": "soil" },
|
||||
{ "id": "soft_rock", "name": "연암", "gamma_t": 24.0, "gamma_sat": 24.5, "c": 150.0, "phi": 35.0, "water": "rock" },
|
||||
{ "id": "hard_rock", "name": "경암", "gamma_t": 26.0, "gamma_sat": 26.0, "c": 300.0, "phi": 40.0, "water": "rock" },
|
||||
{ "id": "fill", "name": "성토", "gamma_t": 18.0, "gamma_sat": 19.0, "c": 10.0, "phi": 28.0, "water": "soil" },
|
||||
{ "id": "colluvium", "name": "붕적토", "gamma_t": 17.5, "gamma_sat": 18.5, "c": 5.0, "phi": 27.0, "water": "soil" }
|
||||
],
|
||||
"label_map": { "soil": "weathered_soil", "ripping_rock": "weathered_rock", "blasting_rock": "soft_rock", "fill": "fill" },
|
||||
"stations": [],
|
||||
"zones": [],
|
||||
"safety_factors": {
|
||||
"dry": { "cut": 1.5, "fill": 1.5, "soft": 1.3 },
|
||||
"wet": { "cut": 1.2, "fill": 1.3, "soft": 1.2 },
|
||||
"seismic": { "cut": 1.1, "fill": 1.1, "soft": 1.1 },
|
||||
"short_term": { "cut": 1.1, "fill": 1.1, "soft": 1.1 }
|
||||
},
|
||||
"road_surcharge_kpa": 10.0,
|
||||
"groundwater": { "dry": "none", "wet_soil": "surface", "wet_rock": "half_height" },
|
||||
"seismic": { "enabled": false, "kh": 0.0 },
|
||||
"limits": {
|
||||
"gamma_t": [10.0, 30.0],
|
||||
"gamma_sat": [10.0, 30.0],
|
||||
"c": [0.0, 1000.0],
|
||||
"phi": [0.0, 60.0],
|
||||
"safety_factor": [1.0, 3.0],
|
||||
"road_surcharge_kpa": [0.0, 100.0],
|
||||
"kh": [0.0, 0.5],
|
||||
"soil_thickness_m": [0.0, 50.0],
|
||||
"chainage_m": [0.0, 100000.0],
|
||||
"name_length": [1, 30]
|
||||
},
|
||||
"sources": {
|
||||
"materials.weathered_soil.gamma_t": "07 §3.3(1) 서울시 「지반조사편람」(2006) 풍화토 17~20 · §3.2(2) 「도로교시방서」 표 〈N값과 단위중량〉 사질토 N 10~30 = 18 · §4 종합 18~19",
|
||||
"materials.weathered_soil.gamma_sat": "07 §3.2(2) 「도로교시방서」 γsat ≈ γ′ + 10 · N 10~30 = 18~19 · §4 종합 19~20",
|
||||
"materials.weathered_soil.c": "07 §3.3(1) 「지반조사편람」 풍화토 비탈면 실무 5~20 kPa · §4 종합 5~20",
|
||||
"materials.weathered_soil.phi": "07 §3.3(1) 「지반조사편람」 풍화토 25~30°(조밀 32°) · §4 종합 27~32",
|
||||
"materials.weathered_rock.gamma_t": "07 §3.3(2) 한국지반공학회 사면안정 학술발표회(1996~2003) 풍화암 19.5~21.0 · §4 종합",
|
||||
"materials.weathered_rock.gamma_sat": "07 §4 종합 풍화암 20.5~22.0",
|
||||
"materials.weathered_rock.c": "07 §3.3(2) 사면안정 학술발표회 비탈면 해석용 풍화암 10~30 kPa(1996 · 2001 = 30)",
|
||||
"materials.weathered_rock.phi": "07 §3.3(2) 사면안정 학술발표회 풍화암 30~36° · §4 종합 30~36",
|
||||
"materials.soft_rock.gamma_t": "07 §4 종합 연암 22~24(범위 20~25) · §3.4(3) Hoek & Bray 연질 퇴적암 실무 22~24",
|
||||
"materials.soft_rock.gamma_sat": "07 §4 종합 연암 23~25",
|
||||
"materials.soft_rock.c": "07 §3.4(1) 한국도로공사 「도로설계실무편람」(1996) 연암 · 보통암(중간 파쇄) 150 kPa",
|
||||
"materials.soft_rock.phi": "07 §3.4(1) 「도로설계실무편람」 연암 · 보통암(중간 파쇄) 35°",
|
||||
"materials.hard_rock.gamma_t": "07 §3.4(3) Hoek & Bray 화성암 · 변성암 실무 25~27 · §4 종합 25~27",
|
||||
"materials.hard_rock.gamma_sat": "07 §4 종합 경암 25.5~27.5",
|
||||
"materials.hard_rock.c": "07 §3.4(2) 「건설공사 비탈면 설계기준 해설」 Bieniawski RMR Ⅱ등급 300~400 kPa · §3.4(1) 도로설계실무편람 양호 경암 200 이상",
|
||||
"materials.hard_rock.phi": "07 §3.4(1) 「도로설계실무편람」 양호한 연암 · 경암 40° · §4 종합 40~45",
|
||||
"materials.fill.gamma_t": "07 §4 종합 성토 18~19 · §3.1 「도로설계요령」 표 〈토사의 개략적인 토질 정수〉 밀실 사질토 19",
|
||||
"materials.fill.gamma_sat": "07 §4 종합 성토 19~20",
|
||||
"materials.fill.c": "07 §4 종합 성토 0~15 kPa(비탈면 실무 5~15 보수 적용)",
|
||||
"materials.fill.phi": "07 §4 종합 성토 28~30 · §3.1 「도로설계요령」 밀실 사질토 30° — 아래값 28",
|
||||
"materials.colluvium.gamma_t": "07 §3.1 「도로설계요령」 밀실치 않은 사질토(붕적토) 17 · §3.2(2) 「도로교시방서」 N < 10 = 17 · §4 종합 17~18",
|
||||
"materials.colluvium.gamma_sat": "07 §3.2(2) 「도로교시방서」 N < 10 γsat 17~18 · §4 종합 18~19",
|
||||
"materials.colluvium.c": "07 §3.3(1) 「지반조사편람」 퇴적토 · 붕적토 0~15 · §4 종합 0~10",
|
||||
"materials.colluvium.phi": "07 §3.1 「도로설계요령」 붕적토 25° · §3.3(1) 25~30 · §4 종합 25~28",
|
||||
"safety_factors.dry": "KDS 11 70 05 표 4.3-1(쌓기 일반) · 4.3-2(연약지반 쌓기) · 4.3-3(깎기) 건기",
|
||||
"safety_factors.wet": "KDS 11 70 05 표 4.3-1 · 4.3-2 · 4.3-3 우기 — 깎기 1.2 = 지하수위 결정 방식(강우침투 해석이면 1.3)",
|
||||
"safety_factors.seismic": "KDS 11 70 05 표 4.3-1 · 4.3-2 · 4.3-3 지진 시",
|
||||
"safety_factors.short_term": "KDS 11 70 05 표 4.3-1 · 4.3-2 · 4.3-3 단기(1년 미만 · 시공 중)",
|
||||
"road_surcharge_kpa": "옹벽 배면 상재하중 10 kN/㎡ 준용(지식DB technical_info 구조물_안정계산 · KDS 11 80 05) · 임도 차량하중 고유 근거 없음",
|
||||
"groundwater.dry": "KDS 11 70 05 표 4.3-3 건기 = 지하수 없음",
|
||||
"groundwater.wet_soil": "KDS 11 70 05 표 4.3-3 우기 토층 · 풍화암 = 지하수위 결정(지표 포화 가정)",
|
||||
"groundwater.wet_rock": "KDS 11 70 05 표 4.3-3 우기 연암 · 경암 = 비탈면 높이 ½ 심도까지 포화",
|
||||
"seismic.enabled": "KDS 11 90 00 1.8.1 ② 영향 범위에 주구조물 없으면 비적용 · 임도는 발주자 협의 — 기본 끔",
|
||||
"seismic.kh": "유사정적 수평지진계수 — 지반가속도의 비율로 사용자 입력(KDS 11 70 05 표 4.3 지진 시)",
|
||||
"stations": "B06 횡단 암 경계(지반선 평행 · 기본 −0.5 m · 물량 가름용 값) — 토사 측점은 경계 없음"
|
||||
}
|
||||
}
|
||||
@@ -138,7 +138,12 @@ def _e01(monkeypatch, rows):
|
||||
return "stab-id"
|
||||
|
||||
monkeypatch.setattr(e01, "_sources", fake_sources)
|
||||
|
||||
async def fake_seed(project_id):
|
||||
seen["seeded"] = project_id
|
||||
|
||||
monkeypatch.setattr(e01, "create_kind_project", fake_create)
|
||||
monkeypatch.setattr(e01, "seed_initial", fake_seed)
|
||||
payload = e01.CreateStabRequest(source_project_id="s" * 36)
|
||||
return seen, lambda: asyncio.run(e01.post_stab_project(None, payload, USER))
|
||||
|
||||
@@ -157,6 +162,7 @@ def test_e01_requires_b06(monkeypatch):
|
||||
assert call() == {"project_id": "stab-id"}
|
||||
assert seen["kind"] == "STABILITY" and seen["source_project_id"] == "s" * 36
|
||||
assert seen["name"] == "임도 안정성평가"
|
||||
assert seen["seeded"] == "stab-id" # E02 초기값(532-3)
|
||||
|
||||
|
||||
def _access(monkeypatch, method, assigned, owner_company=2):
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""532-3 E02 지반 조건 — 기본값 json 꼴 = E03 Types · KDS 표 · 검사 · 측점 적용 거울(파이썬 ↔ TS) ·
|
||||
[저장] · [초기화] · 지층 추가 → 저장 → 다시 읽음(임시 storage · DB 대신 측점 가짜).
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import copy
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from fastapi import HTTPException
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(PROJECT_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(PROJECT_ROOT))
|
||||
|
||||
from E02_StabGround import E02_StabGround_Model as model # noqa: E402
|
||||
from E02_StabGround import E02_StabGround_Router as router # noqa: E402
|
||||
from E03_StabSection import E03_StabSection_Types as types # noqa: E402
|
||||
|
||||
TS = PROJECT_ROOT / "E02_StabGround" / "logic" / "E02_StabGround_Check.ts"
|
||||
TYPESCRIPT = PROJECT_ROOT / "config" / "node_modules" / "typescript"
|
||||
DEFAULTS = model.load_defaults()
|
||||
LIMITS = DEFAULTS["limits"]
|
||||
STATIONS = [
|
||||
{"chainage_m": 0.0, "ground_type": "soil", "soil_thickness_m": None},
|
||||
{"chainage_m": 20.0, "ground_type": "ripping_rock", "soil_thickness_m": 0.5},
|
||||
{"chainage_m": 40.0, "ground_type": "blasting_rock", "soil_thickness_m": 0.8},
|
||||
{"chainage_m": 60.0, "ground_type": "ripping_rock", "soil_thickness_m": 0.5},
|
||||
]
|
||||
|
||||
|
||||
def _ground(**changes):
|
||||
ground = model.build_initial(DEFAULTS, copy.deepcopy(STATIONS))
|
||||
ground.update(changes)
|
||||
return ground
|
||||
|
||||
|
||||
def _bad(mutate):
|
||||
ground = _ground()
|
||||
mutate(ground)
|
||||
return ground
|
||||
|
||||
|
||||
# 거울 시험 판 — 통과 하나 + 틀린 꼴 여럿(NaN 은 JSON 으로 못 넘겨 null · 글 · 참거짓으로)
|
||||
CHECK_CASES = [
|
||||
_ground(),
|
||||
_bad(lambda g: g["materials"][0].update(gamma_t=9.9)),
|
||||
_bad(lambda g: g["materials"][0].update(gamma_sat=17.0)),
|
||||
_bad(lambda g: g["materials"][1].update(c=None, phi="33")),
|
||||
_bad(lambda g: g["materials"][2].update(phi=True, water="sand")),
|
||||
_bad(lambda g: g["materials"][3].update(name=" ", id="soft_rock")),
|
||||
_bad(lambda g: g["materials"][4].update(name="풍화토")),
|
||||
_bad(lambda g: g["materials"][5].update(name="가" * 31)),
|
||||
_bad(lambda g: g["materials"].append(7)),
|
||||
_bad(lambda g: g.update(materials=[])),
|
||||
_bad(lambda g: g["label_map"].update(soil="nope", fill="")),
|
||||
_bad(lambda g: g.update(zones=[{"from_m": 50, "to_m": 50, "soil": "x", "rock": None}])),
|
||||
_bad(
|
||||
lambda g: g.update(
|
||||
zones=[
|
||||
{
|
||||
"from_m": -1,
|
||||
"to_m": 10,
|
||||
"soil": None,
|
||||
"rock": "hard_rock",
|
||||
"soil_thickness_m": 51,
|
||||
},
|
||||
"zone",
|
||||
]
|
||||
)
|
||||
),
|
||||
_bad(lambda g: g["safety_factors"]["wet"].update(cut=0.9, soft=3.1)),
|
||||
_bad(lambda g: g["safety_factors"].pop("seismic")),
|
||||
_bad(lambda g: g.update(road_surcharge_kpa=101)),
|
||||
_bad(lambda g: g["groundwater"].update(wet_rock="full")),
|
||||
_bad(lambda g: g.update(seismic={"enabled": "yes", "kh": 0.6})),
|
||||
_bad(lambda g: g.update(label_map=[], safety_factors=None, groundwater="x")),
|
||||
]
|
||||
|
||||
RESOLVE_CASES = [
|
||||
_ground(),
|
||||
_ground(
|
||||
zones=[
|
||||
{"from_m": 0, "to_m": 20, "soil": "colluvium", "rock": None, "soil_thickness_m": None},
|
||||
{"from_m": 20, "to_m": 50, "soil": None, "rock": "hard_rock", "soil_thickness_m": 2.5},
|
||||
{"from_m": 55, "to_m": 70, "soil": None, "rock": None, "soil_thickness_m": None},
|
||||
]
|
||||
),
|
||||
_ground(
|
||||
zones=[{"from_m": 0, "to_m": 10, "soil": None, "rock": "soft_rock", "soil_thickness_m": 1}]
|
||||
),
|
||||
]
|
||||
|
||||
SCRIPT = r"""
|
||||
const ts = require(process.argv[1]);
|
||||
const fs = require("fs");
|
||||
const src = fs.readFileSync(process.argv[2], "utf8");
|
||||
const opts = { compilerOptions: { module: ts.ModuleKind.CommonJS } };
|
||||
const js = ts.transpileModule(src, opts).outputText;
|
||||
const box = { exports: {} };
|
||||
new Function("exports", "module", js)(box.exports, box);
|
||||
const m = box.exports;
|
||||
const input = JSON.parse(fs.readFileSync(0, "utf8"));
|
||||
process.stdout.write(JSON.stringify({
|
||||
check: input.check.map((g) => m.checkGround(g, input.limits)),
|
||||
resolve: input.resolve.map((g) => m.resolveStations(g)),
|
||||
}));
|
||||
"""
|
||||
|
||||
|
||||
def test_기본값_꼴은_Types_그대로():
|
||||
assert model.check_ground(DEFAULTS, LIMITS) == []
|
||||
assert [m["id"] for m in DEFAULTS["materials"]] == [m["id"] for m in types.DEFAULT_MATERIALS]
|
||||
for item in DEFAULTS["materials"]:
|
||||
assert types.material(item) == {k: item[k] for k in types.Material.__annotations__}
|
||||
for field in model.MATERIAL_FIELDS:
|
||||
assert DEFAULTS["sources"][f"materials.{item['id']}.{field}"].startswith("07 ")
|
||||
assert DEFAULTS["road_surcharge_kpa"] == types.DEFAULT_ROAD_SURCHARGE_KPA == 10.0
|
||||
assert DEFAULTS["groundwater"] == {
|
||||
"dry": "none",
|
||||
"wet_soil": "surface",
|
||||
"wet_rock": "half_height",
|
||||
}
|
||||
assert DEFAULTS["seismic"] == {"enabled": False, "kh": 0.0}
|
||||
# 07 §4 종합 성토 φ 28~30 — 브레인 기본 25 는 범위 밖이라 아래값 28
|
||||
assert next(m for m in DEFAULTS["materials"] if m["id"] == "fill")["phi"] == 28.0
|
||||
assert types.groundwater({"mode": "none"}) == {"mode": "none"}
|
||||
|
||||
|
||||
def test_기준안전율_KDS_11_70_05():
|
||||
assert DEFAULTS["safety_factors"] == {
|
||||
"dry": {"cut": 1.5, "fill": 1.5, "soft": 1.3},
|
||||
"wet": {"cut": 1.2, "fill": 1.3, "soft": 1.2},
|
||||
"seismic": {"cut": 1.1, "fill": 1.1, "soft": 1.1},
|
||||
"short_term": {"cut": 1.1, "fill": 1.1, "soft": 1.1},
|
||||
}
|
||||
|
||||
|
||||
def test_범위_검사():
|
||||
codes = lambda g: {(e["path"], e["code"]) for e in model.check_ground(g, LIMITS)} # noqa: E731
|
||||
assert codes(CHECK_CASES[1]) == {("materials.0.gamma_t", "range")}
|
||||
assert codes(CHECK_CASES[2]) == {("materials.0.gamma_sat", "sat_lt_t")}
|
||||
assert ("materials.4.name", "name_dup") in codes(CHECK_CASES[6])
|
||||
assert ("zones.0.to_m", "order") in codes(CHECK_CASES[11])
|
||||
assert ("zones.0.soil", "ref") in codes(CHECK_CASES[11])
|
||||
assert codes(CHECK_CASES[13]) == {
|
||||
("safety_factors.wet.cut", "range"),
|
||||
("safety_factors.wet.soft", "range"),
|
||||
}
|
||||
assert codes(CHECK_CASES[17]) == {("seismic.enabled", "enum"), ("seismic.kh", "range")}
|
||||
|
||||
|
||||
def test_파이썬과_TS_같음():
|
||||
payload = json.dumps({"limits": LIMITS, "check": CHECK_CASES, "resolve": RESOLVE_CASES})
|
||||
proc = subprocess.run(
|
||||
["node", "-e", SCRIPT, str(TYPESCRIPT), str(TS)],
|
||||
input=payload,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
encoding="utf-8",
|
||||
timeout=60,
|
||||
)
|
||||
assert proc.returncode == 0, proc.stderr
|
||||
out = json.loads(proc.stdout)
|
||||
assert out["check"] == [model.check_ground(g, LIMITS) for g in CHECK_CASES]
|
||||
assert out["resolve"] == [model.resolve_stations(g) for g in RESOLVE_CASES]
|
||||
assert all(out["check"][1:]) # 틀린 판은 모두 걸림
|
||||
|
||||
|
||||
def test_측점_적용():
|
||||
rows = model.resolve_stations(RESOLVE_CASES[1])
|
||||
assert [(r["upper"], r["lower"], r["soil_thickness_m"]) for r in rows] == [
|
||||
("colluvium", None, None), # 토사 측점 · 두께 없음 = 전부 위 지층
|
||||
("colluvium", "hard_rock", 2.5), # 20 = 두 구간 끝 · 뒤 구간이 이김
|
||||
("weathered_soil", "hard_rock", 2.5),
|
||||
("weathered_soil", "weathered_rock", 0.5), # 빈 구간 = B06 그대로
|
||||
]
|
||||
assert [r["zoned"] for r in rows] == [True, True, True, True]
|
||||
assert model.resolve_stations(RESOLVE_CASES[2])[0]["lower"] == "soft_rock"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def project(tmp_path, monkeypatch):
|
||||
async def fake_project(_project_id):
|
||||
return tmp_path, {"company_id": 1, "project_kind": "STABILITY", "source_project_id": None}
|
||||
|
||||
async def fake_stations(_connection, _ids):
|
||||
return copy.deepcopy(STATIONS)
|
||||
|
||||
class FakePool:
|
||||
def acquire(self):
|
||||
class Ctx:
|
||||
async def __aenter__(self):
|
||||
return None
|
||||
|
||||
async def __aexit__(self, *_):
|
||||
return False
|
||||
|
||||
return Ctx()
|
||||
|
||||
monkeypatch.setattr(router, "_project", fake_project)
|
||||
monkeypatch.setattr(router, "_b06_stations", fake_stations)
|
||||
monkeypatch.setattr(router, "get_db_pool", lambda: FakePool())
|
||||
return tmp_path
|
||||
|
||||
|
||||
PID = "9872e9a7-e483-427b-a6b2-72cdbc063bc3"
|
||||
SESSION = {"role": "USER", "company_id": 1}
|
||||
|
||||
|
||||
def test_저장_다시읽음_초기화(project):
|
||||
from uuid import UUID
|
||||
|
||||
pid = UUID(PID)
|
||||
first = asyncio.run(router.get_ground(pid, SESSION))
|
||||
assert first["saved"] is False and first["editable"] is True
|
||||
assert first["ground"]["stations"] == STATIONS
|
||||
assert model.initial_path(project).is_file() and not model.ground_path(project).is_file()
|
||||
|
||||
ground = copy.deepcopy(first["ground"])
|
||||
ground["materials"].append(
|
||||
{
|
||||
"id": "user_1",
|
||||
"name": " 점토층 ",
|
||||
"gamma_t": 16.5,
|
||||
"gamma_sat": 17.5,
|
||||
"c": 20,
|
||||
"phi": 18,
|
||||
"water": "soil",
|
||||
"extra": 1,
|
||||
}
|
||||
)
|
||||
ground["materials"][0]["c"] = 12.0
|
||||
ground["zones"] = [
|
||||
{"from_m": 0, "to_m": 30, "soil": "user_1", "rock": None, "soil_thickness_m": 1.2}
|
||||
]
|
||||
ground["safety_factors"]["wet"]["cut"] = 1.3
|
||||
ground["stations"] = [] # 브라우저 값은 안 받음
|
||||
asyncio.run(router.put_ground(pid, router.GroundRequest(ground=ground)))
|
||||
|
||||
stored = json.loads(model.ground_path(project).read_text(encoding="utf-8"))
|
||||
assert stored["materials"][-1] == {
|
||||
"id": "user_1",
|
||||
"name": "점토층",
|
||||
"gamma_t": 16.5,
|
||||
"gamma_sat": 17.5,
|
||||
"c": 20,
|
||||
"phi": 18,
|
||||
"water": "soil",
|
||||
}
|
||||
assert stored["materials"][0]["c"] == 12.0
|
||||
assert stored["safety_factors"]["wet"]["cut"] == 1.3
|
||||
assert stored["stations"] == STATIONS
|
||||
assert set(stored) == set(model.GROUND_KEYS)
|
||||
for item in stored["materials"]: # 엔진이 바로 읽음
|
||||
types.material(item)
|
||||
|
||||
again = asyncio.run(router.get_ground(pid, SESSION))
|
||||
assert again["saved"] is True and again["ground"] == stored
|
||||
|
||||
bad = copy.deepcopy(stored)
|
||||
bad["materials"][0]["phi"] = 75
|
||||
with pytest.raises(HTTPException) as caught:
|
||||
asyncio.run(router.put_ground(pid, router.GroundRequest(ground=bad)))
|
||||
assert caught.value.status_code == 422
|
||||
assert caught.value.detail["errors"] == [{"path": "materials.0.phi", "code": "range"}]
|
||||
assert json.loads(model.ground_path(project).read_text(encoding="utf-8")) == stored
|
||||
|
||||
reset = asyncio.run(router.reset_ground(pid))
|
||||
initial = json.loads(model.initial_path(project).read_text(encoding="utf-8"))
|
||||
assert reset["ground"] == initial == model.build_initial(DEFAULTS, STATIONS)
|
||||
assert json.loads(model.ground_path(project).read_text(encoding="utf-8")) == initial
|
||||
|
||||
|
||||
def test_다른_회사는_보기만(project):
|
||||
from uuid import UUID
|
||||
|
||||
body = asyncio.run(router.get_ground(UUID(PID), {"role": "USER", "company_id": 2}))
|
||||
assert body["editable"] is False
|
||||
admin = asyncio.run(router.get_ground(UUID(PID), {"role": "SYSTEM_ADMIN", "company_id": None}))
|
||||
assert admin["editable"] is True
|
||||
Reference in New Issue
Block a user