| 327 |
327 |
|
let count = repo.count_unread(user_id).await.expect("Failed to count");
|
| 328 |
328 |
|
assert_eq!(count, 0);
|
| 329 |
329 |
|
}
|
|
330 |
+ |
|
|
331 |
+ |
/// The project-dashboard email list (`list_by_project`) renders only
|
|
332 |
+ |
/// subject/from/date and opens the reader (re-fetching the body via
|
|
333 |
+ |
/// `get_by_id`) on click, so it must return body-less rows -- otherwise every
|
|
334 |
+ |
/// email body materializes into RAM. Mirrors `list_metadata`'s Perf S3 rule.
|
|
335 |
+ |
#[tokio::test]
|
|
336 |
+ |
async fn list_by_project_is_body_less() {
|
|
337 |
+ |
use goingson_core::{NewProject, ProjectRepository};
|
|
338 |
+ |
use goingson_db_sqlite::SqliteProjectRepository;
|
|
339 |
+ |
|
|
340 |
+ |
let pool = common::setup_test_db().await;
|
|
341 |
+ |
let user_id = common::create_test_user(&pool).await;
|
|
342 |
+ |
let projects = SqliteProjectRepository::new(pool.clone());
|
|
343 |
+ |
let emails = SqliteEmailRepository::new(pool);
|
|
344 |
+ |
|
|
345 |
+ |
let project = projects
|
|
346 |
+ |
.create(
|
|
347 |
+ |
user_id,
|
|
348 |
+ |
NewProject {
|
|
349 |
+ |
name: "Dashboard project".into(),
|
|
350 |
+ |
description: String::new(),
|
|
351 |
+ |
project_type: Default::default(),
|
|
352 |
+ |
status: Default::default(),
|
|
353 |
+ |
},
|
|
354 |
+ |
)
|
|
355 |
+ |
.await
|
|
356 |
+ |
.expect("create project");
|
|
357 |
+ |
|
|
358 |
+ |
emails
|
|
359 |
+ |
.create(
|
|
360 |
+ |
user_id,
|
|
361 |
+ |
NewEmail {
|
|
362 |
+ |
project_id: Some(project.id),
|
|
363 |
+ |
from_address: "s@example.com".to_string(),
|
|
364 |
+ |
to_address: "r@example.com".to_string(),
|
|
365 |
+ |
subject: "Linked".to_string(),
|
|
366 |
+ |
body: "a very long body that must not be loaded for a list view".to_string(),
|
|
367 |
+ |
is_read: false,
|
|
368 |
+ |
received_at: Some(Utc::now()),
|
|
369 |
+ |
},
|
|
370 |
+ |
)
|
|
371 |
+ |
.await
|
|
372 |
+ |
.expect("create email");
|
|
373 |
+ |
|
|
374 |
+ |
let listed = emails.list_by_project(user_id, project.id).await.expect("list_by_project");
|
|
375 |
+ |
assert_eq!(listed.len(), 1);
|
|
376 |
+ |
assert_eq!(listed[0].subject, "Linked", "metadata still returned");
|
|
377 |
+ |
assert!(listed[0].body.is_empty(), "body must not be materialized in a list view");
|
|
378 |
+ |
assert!(listed[0].html_body.is_none(), "html body must not be materialized");
|
|
379 |
+ |
assert!(listed[0].body_truncated, "reader must know to re-fetch the full body");
|
|
380 |
+ |
}
|
|
381 |
+ |
|
|
382 |
+ |
/// `list_unlinked` feeds the "link email to project" picker (subject/from only)
|
|
383 |
+ |
/// and must likewise stay body-less so an unbounded mailbox can't blow up RAM.
|
|
384 |
+ |
#[tokio::test]
|
|
385 |
+ |
async fn list_unlinked_is_body_less() {
|
|
386 |
+ |
let pool = common::setup_test_db().await;
|
|
387 |
+ |
let user_id = common::create_test_user(&pool).await;
|
|
388 |
+ |
let repo = SqliteEmailRepository::new(pool);
|
|
389 |
+ |
|
|
390 |
+ |
repo.create(
|
|
391 |
+ |
user_id,
|
|
392 |
+ |
NewEmail {
|
|
393 |
+ |
project_id: None,
|
|
394 |
+ |
from_address: "s@example.com".to_string(),
|
|
395 |
+ |
to_address: "r@example.com".to_string(),
|
|
396 |
+ |
subject: "Unlinked".to_string(),
|
|
397 |
+ |
body: "body that should never be loaded into the picker".to_string(),
|
|
398 |
+ |
is_read: false,
|
|
399 |
+ |
received_at: Some(Utc::now()),
|
|
400 |
+ |
},
|
|
401 |
+ |
)
|
|
402 |
+ |
.await
|
|
403 |
+ |
.expect("create email");
|
|
404 |
+ |
|
|
405 |
+ |
let listed = repo.list_unlinked(user_id).await.expect("list_unlinked");
|
|
406 |
+ |
assert_eq!(listed.len(), 1);
|
|
407 |
+ |
assert_eq!(listed[0].subject, "Unlinked");
|
|
408 |
+ |
assert!(listed[0].body.is_empty(), "body must not be materialized");
|
|
409 |
+ |
assert!(listed[0].body_truncated, "reader must know to re-fetch the full body");
|
|
410 |
+ |
}
|