refactor(saas): make navigation and status idiomatic
Use a real enhanceable Settings URL, remove response-simulated navigation, and replace the one-shot public SSE endpoint with an initial canonical event plus ongoing server-owned updates. Keep a bounded one-event probe for production-reference tests. req: examples/014 req: nav/001 req: nav/002 req: nav/004 req: push/003
This commit is contained in:
@@ -58,6 +58,7 @@ Keep it stable. Prefer pointers to canonical sources over copied structure, file
|
||||
- Use the same Workout command surface for tests, production build, and mobile release: `cargo run -p hemx-xtask -- workout test`, `cargo run -p hemx-xtask -- workout build`, `HEMX_WORKOUT_ORIGIN=https://workout.example.com cargo run -p hemx-xtask -- workout mobile-release`, and `HEMX_WORKOUT_ORIGIN=https://workout.example.com cargo run -p hemx-xtask -- workout mobile-verify`; Android/iOS SDKs, store submission targets, and signing remain external blockers, not repo-owned secrets, and do not imply a broad `hemx-mobile` framework. req: examples/006 req: examples/011 req: examples/013
|
||||
- hemx core stays small: effects, typed ids, registries, and wire schema only; keep features in core only when they fit typed resources plus the closed EffectBatch op set, and treat DOM details as runtime lowering. Workspace crates stay separated, stable-Rust-compatible, and free of kitchen-sink boundaries; new primitives must delete special cases. Public identifiers should flow through typed wrappers over internal `ResourceId`/`ResourceRef`, not special-case opcodes. Wire output lowers symbolic authoring names to compact metadata, the versioned canonical hemx `EffectBatch` codec, postcard surface facts, and form-encoded public requests—not JSON. ABI/schema versions and build fingerprints must guard runtime/server compatibility. v0 scope is the checked hypermedia core plus page/runtime/wire/diagnostic/test/axum proof, not optional sync/wasm/query/auth/router breadth. req: v0_scope/001 req: v0_scope/002 req: v0_scope/005 req: laws/001 req: invariant/001 req: invariant/005 req: typed_id/001 req: typed_id/003 req: effect_algebra/001 req: effect_algebra/006 req: wire/001 req: wire/002 req: wire/003 req: wire/004 req: wire/005 req: wire/006 req: abi/001 req: abi/002 req: abi/003 req: abi/004 req: abi/005 req: misc/001 req: misc/002 req: misc/003 req: misc/004 req: misc/005 req: misc/006 req: misc/007 req: misc/008 req: misc/009 req: misc/010
|
||||
- Routing, auth, sessions, transport, transitions, sync, async data helpers, multipart parsing/uploads, and storage belong in integration/user crates; hemx-axum preserves normal HTTP auth, credentials, CSRF, multipart/browser fallback, and progressive-enhancement semantics rather than defining policy in core. Sync is optional integration state reconciliation over push/transport, not core. req: auth/001 req: auth/002 req: auth/003 req: auth/004 req: auth/005 req: async_data/001 req: async_data/002 req: async_data/003 req: multipart/001 req: multipart/002 req: multipart/003 req: sync/001 req: sync/008
|
||||
- The SaaS production reference must use real links/URLs for navigation and an ongoing server-owned canonical SSE stream; bounded one-event behavior is a test probe, not the public transport contract. req: nav/001 req: nav/002 req: push/003 req: examples/014
|
||||
- Public examples and beginner APIs should use templates plus Rust, generated component APIs, resources, view wrappers, render/page helpers, `#[hemx::app]`, plain `#[hemx::handler]` functions, and `IntoEffect`, not atoms, raw ids, selectors, wire formats, runtime opcodes, manual registries, `$OUT_DIR` includes, raw render/lower calls, raw HTML construction, imperative DOM mutation, or raw effect constructors; keep advanced layers out of starters. req: canonical_authoring/001 req: canonical_authoring/004 req: canonical_authoring/006 req: canonical_authoring/010 req: canonical_authoring/015 req: invariant/003 req: dx/001 req: dx/002 req: dx/010 req: component/003 req: component/004 req: component/005 req: view/001 req: view/002 req: view/003 req: html_safety/001 req: html_safety/003 req: html_safety/005 req: public_api/001 req: public_api/002 req: public_api/003 req: public_api/005 req: public_api/006 req: progressive_disclosure/001 req: progressive_disclosure/002 req: progressive_disclosure/003 req: derive_app/001 req: derive_app/002 req: derive_handler/001 req: derive_handler/002 req: derive_handler/003 req: derive_handler/004 req: derive_handler/005
|
||||
- Typed partial swaps should stay expressed as generated target plus rendered partial plus swap kind, not selector-driven rerendering or response-side selector retargeting; HTTP, page navigation, push, and island behavior adapt around that loop, and docs should layer new primitives progressively. Navigation is an effect/page-swap concern, not a core router framework; enhanced links and GET forms preserve real URL/history semantics so page state stays reloadable/shareable without a client state graph. Push streams carry canonical versioned hemx `EffectBatch` bytes over server-owned SSE/WebSocket transport and keep `data-hemx-sse` root-scoped/same-origin by default. Preserve keyed/optional scope identity for addressable loop nodes, reconcile filtered keyed collections without clearing retained rows, prefer generated keyed-slot helpers over low-level keyed calls, and route self/row-update diagnostics toward local `data-hemx-slot`/`h-key` targets. req: canonical_authoring/002 req: canonical_authoring/014 req: modes/001 req: scope/001 req: list/001 req: list/002 req: list/003 req: list/004 req: list/005 req: list/006 req: nav/001 req: nav/002 req: nav/003 req: nav/004 req: nav/005 req: push/001 req: push/002 req: push/003 req: push/004 req: push/005 req: push/006 req: push/007 req: progressive_disclosure/004 req: page_swap/001 req: page_swap/002 req: page_swap/003 req: locality/001 req: locality/002 req: target_policy/001 req: target_policy/002
|
||||
- `examples/html_examples` is the copy-paste HTML pattern gallery for htmx-style examples; keep exact htmx URL slugs visible while translating behavior to boring `.heml`, generated resources, and server-owned Rust state, not HTMX syntax, selector targeting, or user-authored browser JavaScript. Shared runtime loading and declarative `data-hemx-*` are allowed. Boost containers enhance same-origin descendants only and preserve native external/download/new-tab behavior. req: htmx_equivalents/001 req: htmx_equivalents/003 req: htmx_equivalents/005 req: examples/005 req: examples/007 req: examples/012 req: page_swap/007 req: page_swap/008
|
||||
|
||||
@@ -1,41 +1,21 @@
|
||||
# Active frontier — v1 production readiness
|
||||
# Active frontier — peak idiomatic examples
|
||||
|
||||
**Parent outcome**
|
||||
**Parent ID:** `examples-idiom/001`
|
||||
|
||||
- **User value:** Rust teams can adopt hemx v1 knowing its public contracts, failure recovery, compatibility, dependency policy, and release proof are explicit and reproducible.
|
||||
- **State:** In progress — the product and representative runtime/browser/performance/security paths are implemented, but v1 production readiness is not yet closed.
|
||||
- **Blocked by:** the repository owner must approve a distribution license policy before the legal release gate can close; this does not block mutation hardening or local release verification.
|
||||
- **Non-goals:** no new framework surface, second wire format, WASM runtime decoder, deployment, publication, signing, store submission, or feature expansion.
|
||||
- **Proof:** every child below is done; `docs/v1-readiness.md` reports GO without contradicting `REQUIREMENTS.md`; the tree is committed and clean.
|
||||
- **User value:** A newcomer can move from first app to production reference and advanced integration while seeing one coherent hemx model: plain `.heml`, generated resources, typed Rust handlers/effects, real links/forms/URLs, server-owned truth, native fallback, and explicit leaf adapters.
|
||||
- **State:** Ready — the canonical starter and Workout exemplar are already strong; bounded teaching leaks remain in the HTML gallery, client-local example, SaaS reference, and advanced Kanban boundary.
|
||||
- **Non-goals:** no new framework primitives, client router, VDOM, selector authoring API, reactive expression language, global client store, CSS framework/design system, generalized asset pipeline, visual redesign, or feature expansion. Do not churn `examples/workout` or `examples/techdemo` without a concrete failing contract.
|
||||
- **Build:**
|
||||
- **`examples-idiom/002` — Done: the SaaS reference tells the production truth.** Settings is a real `/settings` link enhanced by the existing page-swap runtime, direct `/settings` renders the fallback page, and `/events` now sends an initial canonical batch followed by ongoing server-owned status updates; `?once` remains only as a bounded production-reference probe. The removed handler no longer simulates navigation with response effects. Raw selector/form construction remains confined to test adapters because generated handles are the asserted boundary, not an app authoring API. req: canonical_authoring/001 req: nav/001 req: nav/002 req: nav/004 req: push/003 req: examples/003 req: examples/004 req: examples/014
|
||||
- **`examples-idiom/003` — Make the HTML pattern gallery mechanically copyable.** In `examples/html_examples/src/main.rs`, generated `gallery` resources, and nearby templates/tests, express the dependent-select flow through typed generated values/partials instead of duplicated string-to-option mapping, and remove request fields discarded only to satisfy example plumbing where the generated handler contract permits it. Preserve each visible htmx slug, native form semantics, server-owned state, inserted-content behavior, and no-reload smoke. req: htmx_equivalents/001 req: htmx_equivalents/003 req: htmx_equivalents/005 req: canonical_authoring/001 req: form/004
|
||||
- **`examples-idiom/004` — Make client-local code visibly a leaf adapter.** In `examples/client_local/src/lib.rs` and its `.heml` surface, project the generated client event/state into a tiny typed counter-domain input/output instead of rendering raw event kind and encoded state as the example's product value. Keep the ordinary `#[hemx::handler(client)]` shape, generated event/state boundary, native event semantics, and no durable client state graph. req: client_local/001 req: client_local/003 req: client_local/004 req: canonical_authoring/017
|
||||
- **`examples-idiom/005` — Separate the advanced Kanban adapter from ordinary hemx app code.** Move the cohesive sync/presence/session/storage transport responsibility from `examples/kanban/src/main.rs` behind one clearly named local integration module with a small route/state contract; keep board templates and ordinary handlers nearby and unchanged where possible. Update `examples/kanban/README.md` to label the fixture as the advanced local/offline/sync north-star, route beginners to the starter/Workout/gallery first, and name legacy `/sync-demo`/`sync.js` as a compatibility probe rather than recommended authoring. Preserve replay, export, deletion, reconnection, auth, and multiplayer browser proof. req: state/001 req: state/002 req: local/001 req: local/002 req: milestone/001 req: sync/001 req: sync/008
|
||||
- **`examples-idiom/006` — Publish and enforce the example ladder.** In `README.md`, example READMEs, and the nearest existing xtask/example checks, identify `app new`/`examples/v0` as the first canonical app, `html_examples` as the pattern gallery, Workout as the product exemplar, SaaS as the production integration reference, `client_local` as the narrow leaf-adapter proof, Techdemo as exhaustive verification, and Kanban as advanced north-star integration. Add the smallest repository-owned guard that fails when beginner/reference authoring regresses to raw IDs, selectors, raw wire/effect constructors, `$OUT_DIR` includes, or app-authored DOM mutation; keep legitimate advanced/test adapters scoped rather than banning tokens globally. req: examples/001 req: examples/003 req: examples/004 req: examples/005 req: examples/007 req: examples/008 req: examples/009 req: canonical_authoring/001 req: progressive_disclosure/001 req: progressive_disclosure/002 req: progressive_disclosure/003
|
||||
- **Blocked by:** none. The separate v1 legal release gate remains blocked on the owner license decision but does not block example work.
|
||||
- **Proof:** each slice must make its user path observable, preserve the named native/recovery path, and pass its focused package/browser proof. Parent closure requires `cargo run -p hemx-xtask -- html-examples-smoke`, `cargo run -p hemx-xtask -- workout test`, focused SaaS/client-local/Kanban package tests, `cargo run -p hemx-xtask -- test`, `cargo check --workspace`, `cargo fmt --check`, `redgate list`, `redgate refs`, and a clean diff. Frustration signals to reject: a beginner must author selectors/raw IDs/wire ops; navigation loses URL/history; JavaScript becomes durable truth; an example claims live/recovery behavior with a one-shot stub; or advanced sync machinery appears to be the default app model.
|
||||
|
||||
## 1. Checkpoint the current robustness slice
|
||||
## Blocked release decision retained
|
||||
|
||||
- [x] **State:** Done — committed as the current robustness slice after full workspace, focused compile-fail, requirements-reference, formatting, and check proof.
|
||||
- **User value:** users receive one explicit typed `EffectBatch` codec and stronger fail-closed form, sync, host, WASM, macro, test-harness, and generated-contract boundaries without parallel magic.
|
||||
- **Build:** review the current diff as one coherent behavior/requirements slice; retain the canonical non-WASM `EffectBatch` codec and remove the duplicate postcard batch API; keep only mutation-driven tests and simplifications that prove public behavior; run the full local test authority; commit the slice.
|
||||
- **Blocked by:** none.
|
||||
- **Proof:** `cargo run -p hemx-xtask -- test`, `cargo fmt --check`, `cargo check --workspace`, `redgate list`, and `redgate refs` pass; `git diff --check` is clean; the focused mutest receipts for the effect codec, `hemx`, `hemx-host`, `hemx-js`, `hemx-wasm`, `hemx-sync`, `hemx-sync-macros`, `hemx-test` public helpers, and `hemx-axum` form/SSE codecs contain no missed mutants; one Conventional Commit records the requirement IDs.
|
||||
|
||||
## 2. Make mutation testing a reproducible release gate
|
||||
|
||||
- [x] **State:** Done — the package-native capped xtask entry point rejects unknown packages and invalid shards, propagates mutest failure, and every mutation-applicable library/proc-macro package passes its complete gate or deterministic shard set with no unexplained survivor.
|
||||
- **User value:** maintainers can run one bounded repository command and trust that meaningful Rust logic across every mutation-applicable library is either killed or explicitly justified.
|
||||
- **Build:** add a capped `hemx-xtask` mutation command that invokes `/opt/repositories/mutest`/`mutest` through package-native test targets rather than the broken workspace-wide example path; enumerate only current mutation-applicable library/proc-macro packages; finish adversarial tests or simplify code until every survivor is classified; keep equivalent, invariant-only, and infrastructure-inapplicable classifications inspectable and minimal; document the exact local release command in the existing readiness surface.
|
||||
- **Blocked by:** none. `hemx-lsp` is binary-only and deliberately outside the mutation-applicable library/proc-macro set elected by test/020; its package tests remain in workspace verification. The runner creates first-use shard output parents, accepts validated one-based shards, maps them to native zero-based shards, uses shard-specific output, grants compiler probes a 120-second floor, and preserves the unsharded gate. Browser-backed `hemx-wasm` runs one mutest worker without a parent jobserver, preventing nested Cargo/Firefox deadlock while preserving the detected Cargo budget; its four shards pass with 59 mutants (52 caught, 7 unviable) after replay projections were batched without changing ordered effect application, Firefox background work was disabled, and the explicit 64-command debug-browser replay budget was set to 250 ms. The complete `hemx` (30 mutants), `hemx-host` (76), `hemx-sync` (164), `hemx-sync-macros` (33), `hemx-axum` (470), `hemx-build` (1,304), and `hemx-derive` (480) gates or deterministic shard sets pass, alongside the previously clean `hemx-core`, `hemx-js`, and `hemx-test` gates.
|
||||
- **Proof:** the new xtask mutation command exits zero within its documented bound, covers each applicable package, emits no unexplained missed mutant, and a deliberate adjacent mutation makes it fail. `cargo run -p hemx-xtask -- test` remains green. req: test/020 req: test/021
|
||||
|
||||
## 3. Elect and enforce the release license policy
|
||||
|
||||
- [ ] **State:** Needs decision
|
||||
- **User value:** adopters can legally evaluate and redistribute hemx with a machine-checked dependency license boundary.
|
||||
- **Build:** owner chooses the repository distribution license and accepted dependency licenses; add the corresponding root license file(s) and minimal `deny.toml`; classify workspace crates and the current dependency set; run strict license, advisory, and source checks; reject unknown/unlicensed dependencies rather than silently broadening policy.
|
||||
- **Blocked by:** owner legal decision: choose the repository license and whether weak-copyleft dependencies are acceptable. Current dependency licenses observed by `cargo deny list` include Apache-2.0, Apache-2.0 WITH LLVM-exception, BSD-3-Clause, BSL-1.0, MIT, Unicode-3.0, Unlicense, plus unlicensed workspace packages because the repository has no elected license.
|
||||
- **Proof:** `cargo deny check licenses advisories sources` exits zero from repository configuration; every allowed license is explicit; the repository license is visible at the root; adding a disallowed/unlicensed fixture fails the gate. req: security/007 req: v1_release/006
|
||||
|
||||
## 4. Issue the final v1 GO/NO-GO decision
|
||||
|
||||
- [ ] **State:** Blocked by slices 1–3
|
||||
- **User value:** users get an honest release candidate whose documented support, recovery, accessibility, performance, examples, and operational behavior match what was actually proven.
|
||||
- **Build:** rerun the complete local release matrix from `docs/v1-readiness.md`, including mutation and license gates; verify the pinned Rust/browser/WASM/Axum compatibility matrix and migration fixture; run browser, offline/reconnect, mixed-deploy recovery, accessibility, security, performance, docs, and canonical-example proofs; independently audit requirement-to-proof coverage and README/readiness contradictions; fix only release-blocking defects; mark GO only when no P0/P1 or unexplained gate failure remains.
|
||||
- **Blocked by:** slices 1–3; publishing/deployment authority remains separate and is not required for a local GO decision.
|
||||
- **Proof:** all commands in `docs/v1-readiness.md` pass from a clean checkout within documented bounds; `redgate list` and `redgate refs` pass; known installed `redgate health`/`lint` corpus-format warnings are either resolved or accurately documented rather than hidden; `docs/v1-readiness.md` says GO and names zero open release blockers; independent contradiction review agrees. req: v1_release/001 req: v1_release/002 req: v1_release/003 req: v1_release/004 req: v1_release/005 req: v1_release/006 req: v1_release/007 req: v1_release/008 req: v1_release/009 req: v1_release/010
|
||||
- [ ] **State:** Needs decision — choose the repository distribution license and approved third-party SPDX set, then add root license file(s), workspace package metadata, `deny.toml`, and rerun the release matrix.
|
||||
- **Blocked by:** owner/legal authority. Current third-party set includes Apache-2.0, Apache-2.0 WITH LLVM-exception, BSD-3-Clause, BSL-1.0, MIT, Unicode-3.0, and Unlicense; all 20 workspace packages currently lack license metadata.
|
||||
- **Proof:** `cargo deny check advisories sources licenses` and the full `docs/v1-readiness.md` matrix pass, then readiness changes from NO-GO to GO without publication or deployment.
|
||||
|
||||
@@ -852,6 +852,8 @@ what a valid business email is. [north_star]
|
||||
|
||||
013 Workout mobile commands must not add a broad `hemx-mobile` framework. [north_star]
|
||||
|
||||
014 The SaaS production reference uses real URL navigation and an ongoing server-owned SSE status stream; its bounded test probe emits the same canonical hemx event shape. [north_star]
|
||||
|
||||
---
|
||||
|
||||
## ms
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
pub mod ui {}
|
||||
|
||||
use hemplate::Hemplate;
|
||||
use hemx::{push, Html, IntoEffect};
|
||||
use hemx::{Html, IntoEffect};
|
||||
use hemx_axum::{
|
||||
interactions, runtime_js_path, Form, HandlerErrorContext, HandlerFailure, IntoHandlerFailure,
|
||||
Registry, State,
|
||||
@@ -565,15 +565,6 @@ mod dashboard_handlers {
|
||||
dashboard::live_status.set(format!("{total} projects persisted locally")),
|
||||
))
|
||||
}
|
||||
|
||||
#[hemx::handler]
|
||||
pub async fn open_settings(State(_ctx): State<AppContext>) -> impl IntoEffect {
|
||||
(
|
||||
dashboard::page_panel.put(&SettingsPage::production_boundaries()),
|
||||
dashboard::nav.set("Settings"),
|
||||
push("/settings"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn live_status(projects: usize) -> impl IntoEffect {
|
||||
@@ -740,23 +731,10 @@ mod tests {
|
||||
assert!(created.updates_text(dashboard::live_status));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn page_swap_and_push_shape_use_generated_targets() {
|
||||
// req: page_swap/002 req: push/003 req: examples/001
|
||||
#[test]
|
||||
fn live_status_uses_the_generated_dashboard_target() {
|
||||
// req: push/003 req: examples/014
|
||||
let ctx = AppContext::demo();
|
||||
let settings = inspect_batch(
|
||||
InteractionRequest::from(form(dashboard::open_settings, &[]))
|
||||
.dispatch_async(registry(ctx.clone()))
|
||||
.await
|
||||
.unwrap()
|
||||
.batch,
|
||||
);
|
||||
assert!(
|
||||
settings.updates_html_containing(dashboard::page_panel, "explicit app integrations")
|
||||
);
|
||||
assert!(settings.updates_text(dashboard::nav));
|
||||
assert!(settings.pushes_to("/settings"));
|
||||
|
||||
let heartbeat = inspect(live_status(ctx.projects().len()));
|
||||
assert!(heartbeat.updates_text(dashboard::live_status));
|
||||
assert!(heartbeat.payload_contains("heartbeat"));
|
||||
|
||||
@@ -5,14 +5,14 @@ use axum::middleware::{self, Next};
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use axum::routing::{get, post};
|
||||
use axum::Router;
|
||||
use futures_util::stream;
|
||||
use futures_util::{stream, StreamExt};
|
||||
use hemx::IntoEffect;
|
||||
use hemx_axum::{runtime_js, runtime_js_path, sse, EffectResponse, InteractionRequest};
|
||||
use hemx_saas_example::{home_page, live_status, registry, settings_page, ui, AppContext};
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::Infallible;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Instant;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@@ -81,16 +81,22 @@ async fn events(
|
||||
Query(params): Query<BTreeMap<String, String>>,
|
||||
State(ctx): State<AppContext>,
|
||||
) -> impl IntoResponse {
|
||||
let count = ctx.projects().len();
|
||||
// The production reference exposes an ongoing server-owned stream; `once`
|
||||
// keeps a bounded probe for package tests without changing the public path.
|
||||
// req: examples/014
|
||||
let event = |ctx: &AppContext| {
|
||||
Ok::<_, Infallible>(live_status(ctx.projects().len()).into_batch(ui::BUILD_FINGERPRINT))
|
||||
};
|
||||
let initial = stream::once(std::future::ready(event(&ctx)));
|
||||
if params.contains_key("once") {
|
||||
return sse(stream::iter([Ok::<_, Infallible>(
|
||||
live_status(count).into_batch(ui::BUILD_FINGERPRINT),
|
||||
)]));
|
||||
return sse(initial.left_stream());
|
||||
}
|
||||
|
||||
sse(stream::iter([Ok::<_, Infallible>(
|
||||
live_status(count).into_batch(ui::BUILD_FINGERPRINT),
|
||||
)]))
|
||||
let updates = stream::unfold(ctx, move |ctx| async move {
|
||||
tokio::time::sleep(Duration::from_secs(15)).await;
|
||||
Some((event(&ctx), ctx))
|
||||
});
|
||||
sse(initial.chain(updates).right_stream())
|
||||
}
|
||||
|
||||
// req: auth/001 req: auth/002 req: auth/004
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<nav class="tabs" data-hemx-slot="nav">
|
||||
<a href="/" data-hemx-nav="">Projects</a>
|
||||
<button type="button" data-hemx-handle="open_settings">Settings</button>
|
||||
<a href="/settings" data-hemx-nav>Settings</a>
|
||||
</nav>
|
||||
|
||||
<section class="panel" data-hemx-slot="page_panel">
|
||||
|
||||
@@ -125,6 +125,25 @@ fn authenticated_project_mutation_is_atomic_and_survives_restart() {
|
||||
);
|
||||
assert!(!home.contains("<script>"));
|
||||
assert!(!home.contains("javascript:"));
|
||||
assert!(
|
||||
home.contains("href=\"/settings\" data-hemx-nav"),
|
||||
"settings must remain a real, enhanceable link"
|
||||
);
|
||||
let settings = request(&address, "GET", "/settings", &[], "");
|
||||
assert!(settings.starts_with("HTTP/1.1 200"), "{settings}");
|
||||
assert!(settings.contains("<h2>Settings</h2>"), "{settings}");
|
||||
assert!(settings.contains("explicit app integrations"), "{settings}");
|
||||
|
||||
let events = request(&address, "GET", "/events?once=1", &[], "");
|
||||
assert!(events.starts_with("HTTP/1.1 200"), "{events}");
|
||||
assert_eq!(
|
||||
response_header(&events, "content-type"),
|
||||
"text/event-stream"
|
||||
);
|
||||
assert!(events.contains("event: hemx"), "{events}");
|
||||
assert!(events.contains("data:"), "{events}");
|
||||
// test req: nav/001 req: nav/002 req: push/003 req: examples/014
|
||||
|
||||
let live = request(&address, "GET", "/health/live", &[], "");
|
||||
assert!(live.starts_with("HTTP/1.1 200"), "{live}");
|
||||
assert!(live.contains("{\"status\":\"live\"}"), "{live}");
|
||||
|
||||
Reference in New Issue
Block a user