Skip to content

Commit e7bfcb0

Browse files
committed
UI/UX improvements
1 parent 857f2d1 commit e7bfcb0

2 files changed

Lines changed: 196 additions & 4 deletions

File tree

.claude/skills/add-test/SKILL.md

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
---
2+
name: add-test
3+
description: Add a new HTTP/1.1 compliance, smuggling, malformed input, or normalization test to Http11Probe. Handles code, docs, dashboard, and table wiring.
4+
---
5+
6+
# Add a New Test to Http11Probe
7+
8+
The user wants to add a new test. Gather the following from them (or from context):
9+
10+
1. **What RFC violation or behavior** the test checks
11+
2. **Which category**: Compliance, Smuggling, MalformedInput, or Normalization
12+
3. **The raw HTTP request** to send (or enough detail to construct it)
13+
4. **Expected outcome**: what status code / behavior means pass, warn, or fail
14+
5. **RFC reference** (if applicable): section number and the exact quote with the MUST/SHOULD/MAY keyword
15+
16+
Then perform **all 6 steps below**, in order.
17+
18+
---
19+
20+
## Step 1 — Add the test case to the suite file
21+
22+
Choose the correct file:
23+
24+
| Category | File |
25+
|----------|------|
26+
| Compliance | `src/Http11Probe/TestCases/Suites/ComplianceSuite.cs` |
27+
| Smuggling | `src/Http11Probe/TestCases/Suites/SmugglingSuite.cs` |
28+
| MalformedInput | `src/Http11Probe/TestCases/Suites/MalformedInputSuite.cs` |
29+
| Normalization | `src/Http11Probe/TestCases/Suites/NormalizationSuite.cs` |
30+
31+
Read the file first to understand existing patterns and find the right insertion point.
32+
33+
Append a `yield return new TestCase { ... };` inside `GetTestCases()`:
34+
35+
```csharp
36+
yield return new TestCase
37+
{
38+
Id = "PREFIX-NAME",
39+
Description = "What this test checks",
40+
Category = TestCategory.XXX,
41+
RfcReference = "RFC 9112 §X.X", // Use § not "Section". Omit if N/A.
42+
RfcLevel = RfcLevel.Must, // Must|Should|May|OughtTo|NotApplicable
43+
Scored = true, // false for MAY/informational tests
44+
PayloadFactory = ctx => MakeRequest(
45+
$"GET / HTTP/1.1\r\nHost: {ctx.HostHeader}\r\n\r\n"
46+
),
47+
Expected = new ExpectedBehavior
48+
{
49+
ExpectedStatus = StatusCodeRange.Exact(400),
50+
},
51+
};
52+
```
53+
54+
**ID prefix conventions:** `COMP-`, `SMUG-`, `MAL-`, `NORM-`, or `RFC9112-X.X-` / `RFC9110-X.X-`
55+
56+
**Validation rules:**
57+
- `Exact(400)` for MUST-400 requirements — NEVER add `AllowConnectionClose = true` for these
58+
- `AllowConnectionClose = true` only when connection close is a valid alternative to a status code
59+
- Use `CustomValidator` for pass/warn/fail logic — always check response BEFORE connection state:
60+
```csharp
61+
CustomValidator = (response, state) =>
62+
{
63+
if (response is not null)
64+
return response.StatusCode == 400 ? TestVerdict.Pass : TestVerdict.Fail;
65+
if (state is ConnectionState.ClosedByServer or ConnectionState.TimedOut)
66+
return TestVerdict.Pass;
67+
return TestVerdict.Fail;
68+
}
69+
```
70+
- `RfcLevel` must match the RFC 2119 keyword. Default is `Must` — only set explicitly for non-Must.
71+
- `Scored = false` only for MAY-level or purely informational tests
72+
- Always use `ctx.HostHeader` in payloads, never hardcoded hosts
73+
74+
**Available helpers:**
75+
- `StatusCodeRange.Exact(int)`, `.Range(int,int)`, `.Range2xx`, `.Range4xx`, `.Range4xxOr5xx`
76+
- `TestVerdict.Pass`, `.Fail`, `.Warn`, `.Skip`, `.Error`
77+
- `ConnectionState.Open`, `.ClosedByServer`, `.TimedOut`, `.Error`
78+
79+
## Step 2 — Add docs URL mapping (if needed)
80+
81+
**File:** `src/Http11Probe.Cli/Reporting/DocsUrlMap.cs`
82+
83+
Only needed for `COMP-*` and `RFC*` prefixed tests. These prefixes are auto-mapped:
84+
- `SMUG-XYZ` → `smuggling/xyz`
85+
- `MAL-XYZ` → `malformed-input/xyz`
86+
- `NORM-XYZ` → `normalization/xyz`
87+
88+
For compliance tests, add to `ComplianceSlugs`:
89+
```csharp
90+
["COMP-EXAMPLE"] = "headers/example",
91+
```
92+
93+
## Step 3 — Create the documentation page
94+
95+
**File:** `docs/content/docs/{category-slug}/{test-slug}.md`
96+
97+
Category slug mapping: `line-endings`, `request-line`, `headers`, `host-header`, `content-length`, `body`, `upgrade`, `smuggling`, `malformed-input`, `normalization`
98+
99+
Use this template:
100+
101+
```markdown
102+
---
103+
title: "TEST NAME"
104+
description: "Test documentation"
105+
weight: 1
106+
---
107+
108+
| | |
109+
|---|---|
110+
| **Test ID** | `PREFIX-NAME` |
111+
| **Category** | Category |
112+
| **RFC** | [RFC 9112 §X.X](https://www.rfc-editor.org/rfc/rfc9112#section-X.X) |
113+
| **Requirement** | MUST |
114+
| **Expected** | `400` or close |
115+
116+
## What it sends
117+
118+
Description of the non-conforming request.
119+
120+
\```http
121+
GET / HTTP/1.1\r\n
122+
Host: localhost:8080\r\n
123+
\r\n
124+
\```
125+
126+
## What the RFC says
127+
128+
> "Exact quote from the RFC." -- RFC 9112 Section X.X
129+
130+
Explanation.
131+
132+
## Why it matters
133+
134+
Security and compatibility implications.
135+
136+
## Sources
137+
138+
- [RFC 9112 §X.X](https://www.rfc-editor.org/rfc/rfc9112#section-X.X)
139+
```
140+
141+
## Step 4 — Add a card to the category index
142+
143+
**File:** `docs/content/docs/{category-slug}/_index.md`
144+
145+
Add inside the `{{</* cards */>}}` block. Scored tests before unscored.
146+
147+
```
148+
{{</* card link="test-slug" title="TEST NAME" subtitle="Short description." */>}}
149+
```
150+
151+
## Step 5 — Add test ID to the website table group
152+
153+
**File:** `docs/content/{compliance,smuggling,malformed-input,normalization}/_index.md`
154+
155+
Find the `GROUPS` array in the `<script>` block and add the new test ID to the appropriate sub-group's `testIds` array.
156+
157+
For example, in `docs/content/compliance/_index.md`:
158+
```javascript
159+
{ key: 'request-parsing', label: 'Request Parsing', testIds: [
160+
'RFC9112-2.2-BARE-LF-REQUEST-LINE', ..., 'NEW-TEST-ID'
161+
]},
162+
```
163+
164+
This controls which table group the test appears in on the results page.
165+
166+
## Step 6 — Add a row to the RFC Requirement Dashboard
167+
168+
**File:** `docs/content/docs/rfc-requirement-dashboard.md`
169+
170+
1. Add a row to the correct table (MUST/SHOULD/MAY/Unscored)
171+
2. Update all counts: summary table, total, suite breakdown, RFC section cross-reference
172+
173+
---
174+
175+
## Verification
176+
177+
After all steps, run:
178+
179+
```bash
180+
dotnet build Http11Probe.slnx -c Release
181+
```
182+
183+
Must compile without errors. Then confirm the test appears in the output of:
184+
185+
```bash
186+
dotnet run --project src/Http11Probe.Cli -- --host localhost --port 8080 --test PREFIX-NAME
187+
```

docs/content/probe-results/_index.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ These results are from CI runs (`ubuntu-latest`). Click a **server name** to vie
1717
{{< /callout >}}
1818

1919
<div id="lang-filter" style="margin-bottom:6px;"></div>
20-
<div id="cat-filter" style="margin-bottom:16px;"></div>
20+
<div id="cat-filter" style="margin-bottom:6px;"></div>
21+
<div id="rfc-level-filter" style="margin-bottom:16px;"></div>
2122
<div id="probe-summary"><p><em>Loading probe data...</em></p></div>
2223

2324
**Pass** — the server gave the correct response. For most tests this means rejecting a malformed request with `400` or closing the connection. For body handling tests it means successfully reading the request body and returning `2xx`.
@@ -38,12 +39,12 @@ These results are from CI runs (`ubuntu-latest`). Click a **server name** to vie
3839
}
3940
var langFiltered = window.PROBE_DATA;
4041
var catFilter = null;
42+
var rfcLevelFilter = null;
4143

4244
function rerender() {
4345
var data = langFiltered;
44-
if (catFilter) {
45-
data = ProbeRender.filterByCategory(data, catFilter);
46-
}
46+
if (catFilter) data = ProbeRender.filterByCategory(data, catFilter);
47+
if (rfcLevelFilter) data = ProbeRender.filterByRfcLevel(data, rfcLevelFilter);
4748
ProbeRender.renderSummary('probe-summary', data);
4849
}
4950

@@ -56,5 +57,9 @@ These results are from CI runs (`ubuntu-latest`). Click a **server name** to vie
5657
catFilter = categories;
5758
rerender();
5859
});
60+
ProbeRender.renderRfcLevelFilter('rfc-level-filter', window.PROBE_DATA, function (l) {
61+
rfcLevelFilter = l;
62+
rerender();
63+
});
5964
})();
6065
</script>

0 commit comments

Comments
 (0)