@@ -8,6 +8,117 @@ use std::sync::{
88 Arc ,
99} ;
1010
11+ #[ tokio:: test]
12+ async fn spotify_shelves_reject_unsupported_filters_before_dispatch ( ) {
13+ let count = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
14+ let seen = count. clone ( ) ;
15+ let app = axum:: Router :: new ( ) . route (
16+ "/spotify/{*path}" ,
17+ axum:: routing:: get (
18+ move |axum:: extract:: Path ( path) : axum:: extract:: Path < String > ,
19+ axum:: extract:: Query ( q) : axum:: extract:: Query <
20+ std:: collections:: HashMap < String , String > ,
21+ > | {
22+ let seen = seen. clone ( ) ;
23+ async move {
24+ match path. as_str ( ) {
25+ "songs" => assert ! ( q. contains_key( "min_release_date" ) ) ,
26+ "library/songs" => {
27+ assert_eq ! ( q. get( "query" ) . map( String :: as_str) , Some ( "probe" ) ) ;
28+ assert ! ( q. keys( ) . all( |key| [
29+ "query" ,
30+ "sort_by" ,
31+ "page_index" ,
32+ "page_limit"
33+ ]
34+ . contains( & key. as_str( ) ) ) ) ;
35+ }
36+ "recommendations" => {
37+ assert ! ( q
38+ . keys( )
39+ . all( |key| [ "page_index" , "page_limit" ] . contains( & key. as_str( ) ) ) )
40+ }
41+ _ => panic ! ( "unexpected shelf route" ) ,
42+ }
43+ seen. fetch_add ( 1 , Ordering :: SeqCst ) ;
44+ axum:: Json ( serde_json:: json!( [ ] ) )
45+ }
46+ } ,
47+ ) ,
48+ ) ;
49+ let listener = tokio:: net:: TcpListener :: bind ( "127.0.0.1:0" ) . await . unwrap ( ) ;
50+ let url = format ! ( "http://{}" , listener. local_addr( ) . unwrap( ) ) ;
51+ let server = tokio:: spawn ( async move { axum:: serve ( listener, app) . await . unwrap ( ) } ) ;
52+ let cgs = plasm_core:: load_schema_dir (
53+ & std:: path:: Path :: new ( env ! ( "CARGO_MANIFEST_DIR" ) ) . join ( "../../apis/appworld/spotify" ) ,
54+ )
55+ . unwrap ( ) ;
56+ let compiled = Arc :: new ( plasm_compile:: compile_cgs_capability_templates ( & cgs) . unwrap ( ) ) ;
57+ let engine = ExecutionEngine :: new ( ExecutionConfig {
58+ base_url : Some ( url) ,
59+ ..Default :: default ( )
60+ } )
61+ . unwrap ( ) ;
62+ for source in [
63+ r#"Song{access_token="fixture",shelf="catalog",min_release_date="2030-01-02T00:00:00"}"# ,
64+ r#"Song{access_token="fixture",shelf="library",query="probe"}"# ,
65+ r#"Song{access_token="fixture",shelf="recommendations"}"# ,
66+ ] {
67+ let expr = plasm_core:: expr_parser:: parse_session_line ( source, & cgs, None )
68+ . unwrap ( )
69+ . expr ;
70+ engine
71+ . execute (
72+ & expr,
73+ & cgs,
74+ & mut SessionMaterialization :: new ( ) ,
75+ None ,
76+ StreamConsumeOpts :: default ( ) ,
77+ ExecuteOptions {
78+ compiled_catalog : Some ( compiled. clone ( ) ) ,
79+ ..Default :: default ( )
80+ } ,
81+ )
82+ . await
83+ . unwrap ( ) ;
84+ }
85+ for ( shelf, input) in [
86+ ( "library" , r#"min_release_date="2030-01-02T00:00:00""# ) ,
87+ ( "library" , r#"genre="rock""# ) ,
88+ ( "recommendations" , r#"query="probe""# ) ,
89+ ( "recommendations" , r#"min_duration=1"# ) ,
90+ ] {
91+ let source = format ! ( r#"Song{{access_token="fixture",shelf="{shelf}",{input}}}"# ) ;
92+ let expr = plasm_core:: expr_parser:: parse_session_line ( & source, & cgs, None )
93+ . unwrap ( )
94+ . expr ;
95+ let error = engine
96+ . execute (
97+ & expr,
98+ & cgs,
99+ & mut SessionMaterialization :: new ( ) ,
100+ None ,
101+ StreamConsumeOpts :: default ( ) ,
102+ ExecuteOptions {
103+ compiled_catalog : Some ( compiled. clone ( ) ) ,
104+ ..Default :: default ( )
105+ } ,
106+ )
107+ . await
108+ . unwrap_err ( ) ;
109+ assert ! (
110+ error. to_string( ) . contains( if shelf == "library" {
111+ "Library songs do not support catalog filters"
112+ } else {
113+ "Recommended songs support pagination only"
114+ } ) ,
115+ "{error}"
116+ ) ;
117+ }
118+ assert_eq ! ( count. load( Ordering :: SeqCst ) , 3 ) ;
119+ server. abort ( ) ;
120+ }
121+
11122#[ tokio:: test]
12123async fn phone_message_variant_contracts_dispatch_only_supported_parameters ( ) {
13124 let count = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
0 commit comments