Skip to content

Add e2e tests, document webmachine→cowmachine differences, correct flow diagram, fix examples#55

Merged
mworrell merged 7 commits intomasterfrom
copilot/add-end-to-end-test-cases
Mar 30, 2026
Merged

Add e2e tests, document webmachine→cowmachine differences, correct flow diagram, fix examples#55
mworrell merged 7 commits intomasterfrom
copilot/add-end-to-end-test-cases

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 30, 2026

Cowmachine lacked end-to-end tests, had no documentation of how it diverged from webmachine, carried a flow diagram with known errors, and had examples that randomly fell back to old Cowboy-direct handlers at runtime.

End-to-end tests (test/cowmachine_e2e_tests.erl)

22 EUnit tests exercising the full HTTP decision flow. The module doubles as a Cowboy middleware and a cowmachine controller; per-test behaviour is driven by application:set_env. Tests are ordered top-to-bottom following the decision diagram:

%% is_authorized returns a binary challenge → 401 with that header
unauthorized_with_challenge_test() ->
    set_opts(#{is_authorized => <<"******"api\"">>}),
    Port = start_listener(e2e_unauthorized_challenge),
    {ok, {{"HTTP/1.1", 401, _}, Headers, _}} = httpc:request(url(Port)),
    {_, Challenge} = lists:keyfind("www-authenticate", 1, Headers),
    ?assertEqual("******"api\"", Challenge), ...

Coverage: 200, HEAD, 503, 414, 405+Allow, 400, 401 (plain + binary challenge), 403, 415, 413, OPTIONS+custom headers, content-type negotiation (match and 406), 404, 410, 301+Location, 307+Location, ETag header, Last-Modified header, If-None-Match→304, If-None-Match:*→304, If-Modified-Since→304, DELETE→204, DELETE→202 (async), POST→200 echo.

Webmachine diff (docs/differences-webmachine-cowmachine.md)

Full reference of every callback difference:

  • Arity: all callbacks Fun(ReqData, State) → {R, RD, S} became Fun(Context) → {R, Context}
  • Removed — replaced by process/4: delete_resource, process_post, named per-content-type handler atoms in content_types_provided/content_types_accepted
  • Renamed: encodings_providedcontent_encodings_provided
  • Added: process/4, transfer_encodings_provided/1, upgrades_provided/1, execute/2
  • Format changes for content_types_provided, allowed_methods (atoms → binaries), charsets_provided (no conversion fun), is_authorized (false now has explicit handling)
  • Flow additions: v3b6_upgrade node; v3h7 semantics differ; v3m20/v3n11/v3o18 dispatch to process/4

Corrected flow diagram (docs/http-headers-status-cowmachine.dot)

Fixes in the existing http-headers-status.dot that reflected webmachine, not cowmachine:

Node Was Now
v3b6_upgrade missing Added — WebSocket/upgrade check between forbidden? and valid_content_headers?
v3m5 edges POST→410, else→v3n5 (reversed) POST→v3n5, else→410
v3n5 edges allow_missing_post=true→410 (reversed) allow_missing_post=true→v3n11
v3m20 delete_resource process(DELETE)
v3h7 any If-Match on missing resource → 412 only If-Match: * → 412

Example fixes

  • 10 startup files (main_app.erl / main.erl): removed rand:uniform switching between old mainpage Cowboy handler and cowmachine; always use #{middlewares => [controller, cowmachine]}
  • hello_world_rest/main_app.erl: fixed compile error (double binding of ProtocolOpts)
  • controller_functions/controller.erl: removed delete_resource/1 (webmachine leftover, never called by cowmachine); fixed finish_request/1 returning {undefined, Context}{true, Context}

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • 8.8.8.8
    • Triggering command: REDACTED, pid is -1 (packet block)
  • repo.hex.pm
    • Triggering command: /usr/lib/erlang/erts-13.2.2.5/bin/inet_gethost 4 (dns block)
  • s3.amazonaws.com
    • Triggering command: /usr/bin/curl curl -fsSL REDACTED -o /usr/local/bin/rebar3 sg-1ubuntu4.6_amd64.deb -libwxgtk3.2-1t64_3.2.4&#43;dfsg-4build1_amd64.deb /usr/local/sbin/iptables main/src/controldpkg-deb main/src/mainpag--control /controller.erl iptables algo�� n_0.21.4-1build3_all.deb ubuntu4.6_all.deb /snap/bin/git ngs/ubuntu-archisystemctl nflict 168.63.129.16 git (dns block)

If you need me to access, download, or install something from one of these locations, you can either:


📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

Copilot AI linked an issue Mar 30, 2026 that may be closed by this pull request
Copilot AI and others added 2 commits March 30, 2026 08:06
Copilot AI changed the title [WIP] Add end-to-end test cases for cowmachine Add e2e tests, document webmachine→cowmachine differences, correct flow diagram, fix examples Mar 30, 2026
Copilot AI requested a review from mworrell March 30, 2026 08:18
@mworrell mworrell marked this pull request as ready for review March 30, 2026 08:21
Copilot AI review requested due to automatic review settings March 30, 2026 08:21
@mworrell
Copy link
Copy Markdown
Member

@copilot Check the tests - some are failing.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR strengthens Cowmachine’s correctness and usability by adding end-to-end coverage of the HTTP decision flow, documenting how Cowmachine diverges from Webmachine, correcting the decision-flow diagram, and making the shipped examples consistently use Cowmachine middleware.

Changes:

  • Added an EUnit-based e2e test module that boots Cowboy listeners and exercises many decision-flow branches via HTTP requests.
  • Added/updated documentation: a Cowmachine-specific decision-flow .dot diagram and a Webmachine→Cowmachine callback-differences reference.
  • Updated multiple examples to consistently run through #{middlewares => [controller, cowmachine]} and removed legacy/random Cowboy-direct fallback paths; fixed a controller example callback.

Reviewed changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
test/cowmachine_e2e_tests.erl New e2e test module covering many decision-flow branches via real HTTP requests.
rebar3.crashdump Adds a crash artifact file (should not be committed).
docs/http-headers-status-cowmachine.dot Adds corrected Cowmachine-specific HTTP decision-flow diagram.
docs/differences-webmachine-cowmachine.md Adds reference doc enumerating callback and behavior differences vs Webmachine.
examples/upload/src/main.erl Removes random switching; always uses Cowmachine middleware chain.
examples/eventsource/src/main.erl Removes random switching; always uses Cowmachine middleware chain.
examples/cookie/src/main.erl Removes random switching; always uses Cowmachine middleware chain.
examples/rest_basic_auth/apps/main/src/main_app.erl Simplifies startup to always use Cowmachine middleware chain.
examples/hello_world_ssl/apps/main/src/main_app.erl Simplifies TLS startup; always uses Cowmachine middleware chain.
examples/hello_world_rest/apps/main/src/main_app.erl Fixes startup (incl. prior compile issue) and always uses Cowmachine middleware chain.
examples/hello_world_chunked/apps/main/src/main_app.erl Simplifies startup; always uses Cowmachine middleware chain (controller2).
examples/echo_post/apps/main/src/main_app.erl Simplifies startup; always uses Cowmachine middleware chain.
examples/echo_get/apps/main/src/main_app.erl Simplifies startup; always uses Cowmachine middleware chain.
examples/compress_response/apps/main/src/main_app.erl Simplifies startup; always uses Cowmachine middleware chain.
examples/controller_functions/apps/main/src/controller.erl Removes unused Webmachine callback and fixes finish_request/1 return value.

mworrell and others added 3 commits March 30, 2026 10:29
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
… preserve www-authenticate header

Agent-Logs-Url: https://github.com/zotonic/cowmachine/sessions/708ab264-012b-4ca9-b558-44bb0d692c0f

Co-authored-by: mworrell <38268+mworrell@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 30, 2026

@copilot Check the tests - some are failing.

Fixed in 4cb6ad7. Both unauthorized_test and unauthorized_with_challenge_test were failing because respond/3 in the decision core throws {stop_request, 401} when no body is pre-set, handing off to the error controller with a fresh Req that doesn't have the www-authenticate header. The fix makes is_authorized/1 in the test controller pre-set a text/plain body when returning a non-true value, so the direct response path is taken and the header is preserved.

@mworrell mworrell merged commit f1fda11 into master Mar 30, 2026
4 checks passed
@mworrell mworrell deleted the copilot/add-end-to-end-test-cases branch March 30, 2026 08:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

End-to-end test cases

3 participants