mod common;
use axum::http::StatusCode;
use common::*;
use what_core::server::create_router;
#[tokio::test]
async fn create_item_via_post_form() {
let proj = TestProject::new();
proj.add_page("index.html", "<h1>Home</h1>");
proj.add_page(
"todos.html",
"<loop data=\"#todos#\" as=\"t\"><li>#t.title#</li></loop>",
);
let (_dir, state) = proj.build_state();
let router = create_router(state);
let resp = post_form(
&router,
"/w-action/todos?w-redirect=/todos",
"title=Buy+milk",
)
.await;
assert_eq!(resp.status, StatusCode::SEE_OTHER);
assert_eq!(resp.location(), Some("/todos"));
let resp2 = get(&router, "/todos").await;
assert_eq!(resp2.status, StatusCode::OK);
resp2.assert_contains("Buy milk");
}
#[tokio::test]
async fn auto_generated_ids() {
let proj = TestProject::new();
proj.add_page("index.html", "<h1>Home</h1>");
proj.add_page(
"todos.html",
"<loop data=\"#todos#\" as=\"t\"><span>#t.id#</span></loop>",
);
let (_dir, state) = proj.build_state();
let router = create_router(state);
post_form(&router, "/w-action/todos?w-redirect=/", "title=First").await;
let resp = get(&router, "/todos").await;
resp.assert_contains(">1<");
}
#[tokio::test]
async fn sequential_ids() {
let proj = TestProject::new();
proj.add_page("index.html", "<h1>Home</h1>");
proj.add_page(
"todos.html",
"<loop data=\"#todos#\" as=\"t\"><span>#t.id#</span></loop>",
);
let (_dir, state) = proj.build_state();
let router = create_router(state);
post_form(&router, "/w-action/todos?w-redirect=/", "title=First").await;
post_form(&router, "/w-action/todos?w-redirect=/", "title=Second").await;
let resp = get(&router, "/todos").await;
resp.assert_contains(">1<");
resp.assert_contains(">2<");
}
#[tokio::test]
async fn collection_available_in_template_loop() {
let proj = TestProject::new();
proj.add_page("index.html", "<h1>Home</h1>");
proj.add_page(
"todos.html",
"<loop data=\"#todos#\" as=\"t\"><li>#t.title#</li></loop>",
);
let (_dir, state) = proj.build_state();
let router = create_router(state);
post_form(&router, "/w-action/todos?w-redirect=/", "title=Alpha").await;
post_form(&router, "/w-action/todos?w-redirect=/", "title=Beta").await;
let resp = get(&router, "/todos").await;
resp.assert_contains("Alpha");
resp.assert_contains("Beta");
}
#[tokio::test]
async fn update_merges_fields() {
let proj = TestProject::new();
proj.add_page("index.html", "<h1>Home</h1>");
proj.add_page(
"todos.html",
"<loop data=\"#todos#\" as=\"t\"><li>#t.title# - #t.status#</li></loop>",
);
let (_dir, state) = proj.build_state_with_config(open_collections_config(&["todos"]));
let router = create_router(state);
post_form(&router, "/w-action/todos?w-redirect=/", "title=Task").await;
post_form(&router, "/w-action/todos/1?w-redirect=/", "status=done").await;
let resp = get(&router, "/todos").await;
resp.assert_contains("Task");
resp.assert_contains("done");
}
#[tokio::test]
async fn update_preserves_unmodified_fields() {
let proj = TestProject::new();
proj.add_page("index.html", "<h1>Home</h1>");
proj.add_page(
"todos.html",
"<loop data=\"#todos#\" as=\"t\"><li>#t.title# by #t.author#</li></loop>",
);
let (_dir, state) = proj.build_state_with_config(open_collections_config(&["todos"]));
let router = create_router(state);
post_form(
&router,
"/w-action/todos?w-redirect=/",
"title=Original&author=Alice",
)
.await;
post_form(&router, "/w-action/todos/1?w-redirect=/", "title=Updated").await;
let resp = get(&router, "/todos").await;
resp.assert_contains("Updated");
resp.assert_contains("Alice"); }
#[tokio::test]
async fn delete_removes_item() {
let proj = TestProject::new();
proj.add_page("index.html", "<h1>Home</h1>");
proj.add_page(
"todos.html",
"<loop data=\"#todos#\" as=\"t\"><li>#t.title#</li></loop>",
);
let (_dir, state) = proj.build_state_with_config(open_collections_config(&["todos"]));
let router = create_router(state);
post_form(&router, "/w-action/todos?w-redirect=/", "title=Remove+me").await;
post_form(
&router,
"/w-action/todos/1?w-action=delete&w-redirect=/",
"",
)
.await;
let resp = get(&router, "/todos").await;
resp.assert_not_contains("Remove me");
}
#[tokio::test]
async fn delete_nonexistent_is_safe() {
let proj = TestProject::new();
proj.add_page("index.html", "<h1>Home</h1>");
let (_dir, state) = proj.build_state();
let router = create_router(state);
let resp = post_form(
&router,
"/w-action/todos/999?w-action=delete&w-redirect=/",
"",
)
.await;
assert_eq!(resp.status, StatusCode::SEE_OTHER);
}
#[tokio::test]
async fn redirect_after_action_custom() {
let proj = TestProject::new();
proj.add_page("index.html", "<h1>Home</h1>");
proj.add_page("success.html", "<h1>Success</h1>");
let (_dir, state) = proj.build_state();
let router = create_router(state);
let resp = post_form(&router, "/w-action/todos?w-redirect=/success", "title=Test").await;
assert_eq!(resp.status, StatusCode::SEE_OTHER);
assert_eq!(resp.location(), Some("/success"));
}
#[tokio::test]
async fn redirect_after_action_default() {
let proj = TestProject::new();
proj.add_page("index.html", "<h1>Home</h1>");
let (_dir, state) = proj.build_state();
let router = create_router(state);
let resp = post_form(&router, "/w-action/todos", "title=Test").await;
assert_eq!(resp.status, StatusCode::SEE_OTHER);
assert_eq!(resp.location(), Some("/"));
}
#[tokio::test]
async fn id_after_delete_no_collision() {
let proj = TestProject::new();
proj.add_page("index.html", "<h1>Home</h1>");
proj.add_page(
"todos.html",
"<loop data=\"#todos#\" as=\"t\"><span>#t.id#</span></loop>",
);
let (_dir, state) = proj.build_state_with_config(open_collections_config(&["todos"]));
let router = create_router(state);
post_form(&router, "/w-action/todos?w-redirect=/", "title=A").await;
post_form(&router, "/w-action/todos?w-redirect=/", "title=B").await;
post_form(&router, "/w-action/todos?w-redirect=/", "title=C").await;
post_form(
&router,
"/w-action/todos/2?w-action=delete&w-redirect=/",
"",
)
.await;
post_form(&router, "/w-action/todos?w-redirect=/", "title=D").await;
let resp = get(&router, "/todos").await;
resp.assert_contains(">4<");
resp.assert_not_contains(">2<");
}