test(library): pin collections, both as a library and as an item
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 24m24s
🏗️ Build and Test JellyTau / Supply Chain (push) Successful in 1m3s
📱 Test APK / Build test APK (push) Successful in 48m0s
Publish Documentation / Build & publish docs to gitea-pages (push) Successful in 7m55s
Traceability Validation / Check Requirement Traces (push) Successful in 23s
🏗️ Build and Test JellyTau / Android Compile Check (push) Successful in 6m26s
🏗️ Build and Test JellyTau / Run Tests (push) Successful in 24m24s
🏗️ Build and Test JellyTau / Supply Chain (push) Successful in 1m3s
📱 Test APK / Build test APK (push) Successful in 48m0s
Publish Documentation / Build & publish docs to gitea-pages (push) Successful in 7m55s
Traceability Validation / Check Requirement Traces (push) Successful in 23s
🏗️ Build and Test JellyTau / Android Compile Check (push) Successful in 6m26s
"Is Collections broken too?" deserved an answer from the suite rather than from reading the query. Both, and they behave differently. A Collections *library* was hit by the same defect as Books and Photos -- unmapped collection_type, no include_item_types, so the library clause matched every cached row -- and is fixed by the same change. The unknown-type test now covers boxsets, photos, homevideos and the empty collection_type Jellyfin sends for a mixed library, instead of standing on books alone. An individual collection is a different path and keeps working: a BoxSet's members carry parent_id, which the cache does store, so they match the ordinary parent link rather than the library clause. That is worth its own test because narrowing the clause could plausibly have taken collections with it, and "Collections is empty" would look identical to the bug being fixed.
This commit is contained in:
@@ -819,6 +819,7 @@ Internal architecture, components, and application logic.
|
|||||||
| UT-246 | Opening a PiP window disarms background audio, and a background signal arriving with the native PiP flag false is still treated as PiP while the frontend's latch says the window is open — without resurrecting one it has already seen close | DR-266 | Done |
|
| UT-246 | Opening a PiP window disarms background audio, and a background signal arriving with the native PiP flag false is still treated as PiP while the frontend's latch says the window is open — without resurrecting one it has already seen close | DR-266 | Done |
|
||||||
| UT-247 | A library whose `collection_type` has no mapping — Books, Photos, a mixed library — does not return the server's films, albums and shows from cache | DR-277 | Done |
|
| UT-247 | A library whose `collection_type` has no mapping — Books, Photos, a mixed library — does not return the server's films, albums and shows from cache | DR-277 | Done |
|
||||||
| UT-248 | Narrowing the library clause does not starve the libraries that do have landing pages: music, movies and TV each still list their own media and none of the others | DR-277 | Done |
|
| UT-248 | Narrowing the library clause does not starve the libraries that do have landing pages: music, movies and TV each still list their own media and none of the others | DR-277 | Done |
|
||||||
|
| UT-249 | Opening an individual collection still lists its own children: a BoxSet's members are matched by the stored `parent_id`, not by the library clause, so narrowing that clause did not empty collections | DR-277 | Done |
|
||||||
### Integration Tests
|
### Integration Tests
|
||||||
|
|
||||||
| Test ID | Test Description | Traces To | Status |
|
| Test ID | Test Description | Traces To | Status |
|
||||||
|
|||||||
@@ -5037,15 +5037,20 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_get_items_unknown_library_type_does_not_return_whole_server() {
|
async fn test_get_items_unknown_library_type_does_not_return_whole_server() {
|
||||||
let db = create_test_db();
|
let db = create_test_db();
|
||||||
seed_library(&db, "books-lib", "books").await;
|
|
||||||
|
|
||||||
insert_item(&db, "movie-1", "Movie", None, None, None).await;
|
insert_item(&db, "movie-1", "Movie", None, None, None).await;
|
||||||
insert_item(&db, "album-1", "MusicAlbum", None, None, None).await;
|
insert_item(&db, "album-1", "MusicAlbum", None, None, None).await;
|
||||||
insert_item(&db, "series-1", "Series", None, None, None).await;
|
insert_item(&db, "series-1", "Series", None, None, None).await;
|
||||||
|
|
||||||
|
// Every library kind the app has no landing page for, including the
|
||||||
|
// empty `collection_type` Jellyfin sends for a mixed library.
|
||||||
|
for collection_type in ["books", "boxsets", "photos", "homevideos", ""] {
|
||||||
|
let lib = format!("lib-{collection_type}");
|
||||||
|
seed_library(&db, &lib, collection_type).await;
|
||||||
|
|
||||||
let repo = make_repo(&db);
|
let repo = make_repo(&db);
|
||||||
let ids: Vec<String> = repo
|
let ids: Vec<String> = repo
|
||||||
.get_items("books-lib", None)
|
.get_items(&lib, None)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.items
|
.items
|
||||||
@@ -5054,10 +5059,56 @@ mod tests {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
assert!(
|
assert!(
|
||||||
!ids.contains(&"movie-1".to_string())
|
ids.is_empty(),
|
||||||
&& !ids.contains(&"album-1".to_string())
|
"a '{collection_type}' library must not serve the server's films, \
|
||||||
&& !ids.contains(&"series-1".to_string()),
|
albums and shows; got {:?}",
|
||||||
"a Books library must not serve the server's films, albums and shows; got {:?}",
|
ids
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opening an individual collection is a different path and must keep
|
||||||
|
/// working: a BoxSet's children carry `parent_id`, which the cache does
|
||||||
|
/// store, so they are matched by the ordinary parent link rather than by
|
||||||
|
/// the library clause this fix narrowed.
|
||||||
|
///
|
||||||
|
/// Worth pinning separately — narrowing the library clause could plausibly
|
||||||
|
/// have taken collections with it, and "Collections is empty" would look
|
||||||
|
/// identical to the bug it was meant to fix.
|
||||||
|
///
|
||||||
|
/// TRACES: UR-007 | DR-277 | UT-249
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_get_items_collection_lists_its_own_children() {
|
||||||
|
let db = create_test_db();
|
||||||
|
seed_library(&db, "boxset-lib", "boxsets").await;
|
||||||
|
|
||||||
|
insert_item(&db, "boxset-1", "BoxSet", None, None, None).await;
|
||||||
|
insert_item(&db, "outsider", "Movie", None, None, None).await;
|
||||||
|
|
||||||
|
// A film inside the collection: linked by parent_id, which is what a
|
||||||
|
// BoxSet's children actually carry.
|
||||||
|
db.execute(Query::with_params(
|
||||||
|
"INSERT INTO items (id, server_id, name, item_type, parent_id, synced_at) \
|
||||||
|
VALUES ('in-set', 'test-server', 'In The Set', 'Movie', ?1, '2024-01-01')",
|
||||||
|
vec![QueryParam::String("boxset-1".to_string())],
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let repo = make_repo(&db);
|
||||||
|
let ids: Vec<String> = repo
|
||||||
|
.get_items("boxset-1", None)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.items
|
||||||
|
.iter()
|
||||||
|
.map(|i| i.id.clone())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
ids,
|
||||||
|
vec!["in-set".to_string()],
|
||||||
|
"a collection lists its own children and nothing else; got {:?}",
|
||||||
ids
|
ids
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user