Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@
- 코드 예제와 함께 상세한 설명 제공
- 한국 고객 환경에 특화된 가이드 작성

## 🧪 실습 랩

문서만 읽는 가이드와 달리, 실제 Azure 리소스를 배포해 직접 돌려 보는 실습입니다. 각 랩은 자체 `azure.yaml`을 가지고 있어 `azd up` 한 번으로 환경이 만들어집니다.

| 랩 | 무엇을 확인하나 | 소요 시간 | 과금 |
|---|---|---|---|
| [Azure SRE Agent 이벤트 기반 장애 분석](monitor/sre-agent-event-lab/README.md) | 장애를 세 번 주입하고 Azure Monitor 경고를 받은 SRE Agent가 원인을 짚어내는지 | 배포 5분 + 시나리오당 30~40분 | 있음 (Container Apps, Log Analytics, Storage 등) |

각 랩의 README가 사전 조건, 배포, 정리 절차를 안내합니다. **실습을 마치면 반드시 각 랩의 정리 절차를 따라 리소스를 삭제하세요.**

새 랩을 추가할 때는 `azure.yaml`과 README를 갖춘 디렉터리를 만들고 위 표에 행을 추가합니다.

## 📝 기여 방법

### 새로운 가이드 추가하기
Expand Down
122 changes: 122 additions & 0 deletions monitor/sre-agent-event-lab/scripts/tests/test_repo_readme.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
"""Contract tests for the repository README's lab index.

The root README is where someone arriving at this repository looks first,
so a lab that can actually be deployed and run has to be reachable from
there. These tests keep that index honest as labs are added: every runnable
lab is listed exactly once, every link resolves, and the columns a reader
uses to decide whether to start (cost, time) are filled in.

A "runnable lab" is defined the same way the repository already
distinguishes one: a directory with its own `azure.yaml`, i.e. something
`azd up` can provision. Documentation-only guides are not labs and are not
listed here.
"""
import re
from functools import lru_cache
from pathlib import Path

Comment thread
hellices marked this conversation as resolved.

REPO_ROOT = Path(__file__).parents[4]
README = REPO_ROOT / "README.md"

LAB_SECTION_HEADING = "## 🧪 실습 랩"

# Directories that are never scanned for labs: build output, virtual
# environments, and the worktrees this repository's own workflow creates.
IGNORED_PARTS = {
".git",
".worktrees",
".venv",
"node_modules",
"__pycache__",
}


@lru_cache(maxsize=1)
def runnable_labs():
"""Every directory an operator can provision with `azd up`.

Cached: this walks the whole repository, and every test below needs the
same answer.
"""
labs = []
for azure_yaml in REPO_ROOT.rglob("azure.yaml"):
relative = azure_yaml.relative_to(REPO_ROOT)
if IGNORED_PARTS & set(relative.parts):
continue
labs.append(relative.parent)
return tuple(sorted(labs))


def lab_section():
text = README.read_text()
assert LAB_SECTION_HEADING in text, (
"the README has no lab index; a reader cannot find what is runnable"
)
start = text.index(LAB_SECTION_HEADING)
rest = text[start + len(LAB_SECTION_HEADING):]
end = rest.find("\n## ")
return rest if end == -1 else rest[:end]


def linked_paths(section):
"""Every relative Markdown link target in the section."""
return re.findall(r"\]\((?!https?://)([^)#]+)", section)


def test_every_runnable_lab_is_listed():
labs = runnable_labs()
assert labs, "no runnable lab found; the discovery rule is wrong"

section = lab_section()
for lab in labs:
assert str(lab) in section, (
"{0} can be provisioned but is missing from the README index".format(lab)
)


def test_a_lab_is_listed_only_once():
"""A duplicated row is how an index starts drifting from reality."""
section = lab_section()
for lab in runnable_labs():
rows = [
line
for line in section.splitlines()
if line.startswith("|") and str(lab) in line
]
assert len(rows) == 1, (lab, rows)


def test_every_link_in_the_index_resolves():
section = lab_section()
targets = linked_paths(section)
assert targets, "the index lists no navigable entry"
for target in targets:
assert (REPO_ROOT / target.strip()).exists(), target


def test_each_row_states_the_cost_and_the_time_commitment():
"""Both labs cost money and take time; a reader decides on those two
facts before opening anything."""
section = lab_section()
header = next(line for line in section.splitlines() if line.startswith("|"))
for column in ("소요", "과금"):
assert column in header, column

rows = [
line
for line in section.splitlines()
if line.startswith("|") and not set(line) <= set("|- ")
][1:]
assert rows, "the index has a header but no entries"
for row in rows:
cells = [cell.strip() for cell in row.strip("|").split("|")]
assert all(cells), row


def test_the_index_explains_how_to_add_a_lab():
"""The list only stays current if the next person knows it exists."""
section = lab_section()
assert "azure.yaml" in section, (
"the section must state what qualifies as a lab, so a new one is added here"
)