feat(b08): 라이브러리 관리자 발행 — 프로그램 기본도 storage 작업본(git resources 는 씨앗 · 없는 코드만 · 덮어쓰기 금지) · [회사 라이브러리에 발행](마스터·시스템 관리자) · [프로그램 기본으로 발행](시스템 관리자) — [내 라이브러리에 저장]과 같은 모양 · 권한은 서버가 can_publish 로 내리고 화면은 그대로만 보임
화면(시스템 관리자 계정): 두 단추 보임 · 확인창 「모든 회사가 쓰는 프로그램 기본에 발행 — 같은 종류가 있으면 덮어씀」 · 공용 storage 라 취소로 멈춤(쓰기 없음 확인) · 쓰기는 시험(임시 storage)으로 잼 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
This commit is contained in:
@@ -200,6 +200,67 @@ def test_내_라이브러리에_저장은_고친_식을_담고_프로젝트는_
|
||||
assert _mortar(_sheet(client))["unit_amount"] == pytest.approx(amount)
|
||||
|
||||
|
||||
def test_프로그램_기본은_storage_작업본이_씨앗을_이긴다(storage: Path) -> None:
|
||||
"""관리자 UI(2026-09-14 브레인) — 프로그램 기본도 storage 작업본 · git 파일은 씨앗(없는 코드만)."""
|
||||
seed = load_template("masonry_wet")
|
||||
assert library_module.program_items()[0]["code"] == seed["code"] # 작업본 없으면 씨앗
|
||||
edited = {**seed, "name": "돌쌓기(찰) 관리자 고침"}
|
||||
folder = library_module.program_library_dir()
|
||||
folder.mkdir(parents=True)
|
||||
(folder / f"{seed['code']}.json").write_text(json.dumps(edited), encoding="utf-8")
|
||||
items = library_module.program_items()
|
||||
assert [i["name"] for i in items if i["type_id"] == "masonry_wet"] == ["돌쌓기(찰) 관리자 고침"]
|
||||
assert load_template("masonry_wet")["name"] == "돌쌓기(찰) 관리자 고침"
|
||||
assert str(folder).startswith(str(storage)) # 씨앗(git) 파일은 안 건드림
|
||||
|
||||
|
||||
def test_발행은_권한대로_회사_기본_단에_같은_모양으로(client: TestClient, storage: Path) -> None:
|
||||
sheet = _sheet(client)
|
||||
denied = client.put(
|
||||
f"{SHEETS}/library/publish", json={"sheet_key": sheet["key"], "tier": "program"}
|
||||
)
|
||||
assert denied.status_code == 403 # 시스템 관리자만
|
||||
assert client.get(f"{SHEETS}/library", params={"type_id": "masonry_wet"}).json()[
|
||||
"can_publish"
|
||||
] == {"company": False, "program": False}
|
||||
|
||||
client.app.dependency_overrides[verify_session] = lambda: {
|
||||
"company_id": 7,
|
||||
"user_id": 42,
|
||||
"is_master": True,
|
||||
"role": "ADMIN",
|
||||
}
|
||||
company = client.put(
|
||||
f"{SHEETS}/library/publish", json={"sheet_key": sheet["key"], "tier": "company"}
|
||||
)
|
||||
assert company.status_code == 200, company.text
|
||||
saved = json.loads(
|
||||
(storage / "7" / "library" / f"{company.json()['code']}.json").read_text("utf-8")
|
||||
)
|
||||
assert saved["library_tier"] == "company"
|
||||
assert (
|
||||
client.put(
|
||||
f"{SHEETS}/library/publish", json={"sheet_key": sheet["key"], "tier": "program"}
|
||||
).status_code
|
||||
== 403
|
||||
)
|
||||
|
||||
client.app.dependency_overrides[verify_session] = lambda: {
|
||||
"company_id": None,
|
||||
"user_id": 1,
|
||||
"role": "SYSTEM_ADMIN",
|
||||
}
|
||||
program = client.put(
|
||||
f"{SHEETS}/library/publish", json={"sheet_key": sheet["key"], "tier": "program"}
|
||||
)
|
||||
assert program.status_code == 200, program.text
|
||||
assert program.json()["code"] == load_template("masonry_wet")["code"] # 씨앗 코드로 덮어씀
|
||||
assert (library_module.program_library_dir() / f"{program.json()['code']}.json").is_file()
|
||||
assert client.get(f"{SHEETS}/library", params={"type_id": "masonry_wet"}).json()["can_publish"][
|
||||
"program"
|
||||
]
|
||||
|
||||
|
||||
def test_회사_없는_사람은_개인_단을_못_쓴다(client: TestClient) -> None:
|
||||
client.app.dependency_overrides[verify_session] = lambda: {"company_id": None, "user_id": 1}
|
||||
sheet = _sheet(client)
|
||||
@@ -277,6 +338,9 @@ def test_복제_단추는_개인_단_항목에선_안_눌린다() -> None:
|
||||
)
|
||||
assert "/clone" in ui and "복제해서 내 것으로" in ui
|
||||
assert 'clone.disabled = tier === "personal"' in ui
|
||||
# 발행 단추 — 서버 권한(can_publish)대로만 보임
|
||||
assert "/publish" in ui and "toCompany.hidden = !canPublish?.company" in ui
|
||||
assert "toProgram.hidden = !canPublish?.program" in ui
|
||||
|
||||
|
||||
def test_코드_모양이_아니면_박지_않는다(project: Path) -> None:
|
||||
|
||||
Reference in New Issue
Block a user