@@ -99,7 +99,7 @@ fn extract_psr4_entries(prefix: &str, paths: &serde_json::Value, mappings: &mut
9999}
100100
101101/// Normalise a directory path: ensure it uses forward slashes and ends with `/`.
102- fn normalise_path ( path : & str ) -> String {
102+ pub fn normalise_path ( path : & str ) -> String {
103103 let p = path. replace ( '\\' , "/" ) ;
104104 if p. ends_with ( '/' ) || p. is_empty ( ) {
105105 p
@@ -177,351 +177,3 @@ fn is_builtin_type(name: &str) -> bool {
177177 | "iterable"
178178 )
179179}
180-
181- // ─── Tests ──────────────────────────────────────────────────────────────────
182-
183- #[ cfg( test) ]
184- mod tests {
185- use super :: * ;
186- use std:: fs;
187-
188- /// Helper: create a temporary workspace with a composer.json and
189- /// optional PHP class files.
190- struct TestWorkspace {
191- dir : tempfile:: TempDir ,
192- }
193-
194- impl TestWorkspace {
195- fn new ( composer_json : & str ) -> Self {
196- let dir = tempfile:: tempdir ( ) . expect ( "failed to create temp dir" ) ;
197- fs:: write ( dir. path ( ) . join ( "composer.json" ) , composer_json)
198- . expect ( "failed to write composer.json" ) ;
199- TestWorkspace { dir }
200- }
201-
202- fn root ( & self ) -> & Path {
203- self . dir . path ( )
204- }
205-
206- /// Create a PHP file at the given relative path with minimal content.
207- fn create_php_file ( & self , relative_path : & str , content : & str ) {
208- let full_path = self . dir . path ( ) . join ( relative_path) ;
209- if let Some ( parent) = full_path. parent ( ) {
210- fs:: create_dir_all ( parent) . expect ( "failed to create dirs" ) ;
211- }
212- fs:: write ( & full_path, content) . expect ( "failed to write PHP file" ) ;
213- }
214- }
215-
216- #[ test]
217- fn test_parse_basic_psr4 ( ) {
218- let ws = TestWorkspace :: new (
219- r#"{
220- "autoload": {
221- "psr-4": {
222- "Klarna\\": "src/Klarna/"
223- }
224- }
225- }"# ,
226- ) ;
227-
228- let mappings = parse_composer_json ( ws. root ( ) ) ;
229- assert_eq ! ( mappings. len( ) , 1 ) ;
230- assert_eq ! ( mappings[ 0 ] . prefix, "Klarna\\ " ) ;
231- assert_eq ! ( mappings[ 0 ] . base_path, "src/Klarna/" ) ;
232- }
233-
234- #[ test]
235- fn test_parse_autoload_dev ( ) {
236- let ws = TestWorkspace :: new (
237- r#"{
238- "autoload": {
239- "psr-4": {
240- "Klarna\\": "src/Klarna/"
241- }
242- },
243- "autoload-dev": {
244- "psr-4": {
245- "Klarna\\Rest\\Tests\\": "tests/"
246- }
247- }
248- }"# ,
249- ) ;
250-
251- let mappings = parse_composer_json ( ws. root ( ) ) ;
252- assert_eq ! ( mappings. len( ) , 2 ) ;
253-
254- // Longest prefix first
255- assert_eq ! ( mappings[ 0 ] . prefix, "Klarna\\ Rest\\ Tests\\ " ) ;
256- assert_eq ! ( mappings[ 0 ] . base_path, "tests/" ) ;
257- assert_eq ! ( mappings[ 1 ] . prefix, "Klarna\\ " ) ;
258- assert_eq ! ( mappings[ 1 ] . base_path, "src/Klarna/" ) ;
259- }
260-
261- #[ test]
262- fn test_parse_array_paths ( ) {
263- let ws = TestWorkspace :: new (
264- r#"{
265- "autoload": {
266- "psr-4": {
267- "App\\": ["src/", "lib/"]
268- }
269- }
270- }"# ,
271- ) ;
272-
273- let mappings = parse_composer_json ( ws. root ( ) ) ;
274- assert_eq ! ( mappings. len( ) , 2 ) ;
275- assert_eq ! ( mappings[ 0 ] . prefix, "App\\ " ) ;
276- assert_eq ! ( mappings[ 0 ] . base_path, "src/" ) ;
277- assert_eq ! ( mappings[ 1 ] . prefix, "App\\ " ) ;
278- assert_eq ! ( mappings[ 1 ] . base_path, "lib/" ) ;
279- }
280-
281- #[ test]
282- fn test_parse_no_composer_json ( ) {
283- let dir = tempfile:: tempdir ( ) . expect ( "failed to create temp dir" ) ;
284- let mappings = parse_composer_json ( dir. path ( ) ) ;
285- assert ! ( mappings. is_empty( ) ) ;
286- }
287-
288- #[ test]
289- fn test_parse_invalid_json ( ) {
290- let ws = TestWorkspace :: new ( "not valid json {{{" ) ;
291- let mappings = parse_composer_json ( ws. root ( ) ) ;
292- assert ! ( mappings. is_empty( ) ) ;
293- }
294-
295- #[ test]
296- fn test_parse_no_psr4_section ( ) {
297- let ws = TestWorkspace :: new (
298- r#"{
299- "name": "vendor/project",
300- "autoload": {
301- "classmap": ["src/"]
302- }
303- }"# ,
304- ) ;
305-
306- let mappings = parse_composer_json ( ws. root ( ) ) ;
307- assert ! ( mappings. is_empty( ) ) ;
308- }
309-
310- #[ test]
311- fn test_resolve_simple_class ( ) {
312- let ws = TestWorkspace :: new (
313- r#"{
314- "autoload": {
315- "psr-4": {
316- "Klarna\\": "src/Klarna/"
317- }
318- }
319- }"# ,
320- ) ;
321- ws. create_php_file (
322- "src/Klarna/Customer.php" ,
323- "<?php\n namespace Klarna;\n class Customer {}\n " ,
324- ) ;
325-
326- let mappings = parse_composer_json ( ws. root ( ) ) ;
327- let result = resolve_class_path ( & mappings, ws. root ( ) , "Klarna\\ Customer" ) ;
328-
329- assert ! ( result. is_some( ) ) ;
330- let path = result. unwrap ( ) ;
331- assert ! ( path. ends_with( "src/Klarna/Customer.php" ) ) ;
332- }
333-
334- #[ test]
335- fn test_resolve_nested_namespace ( ) {
336- let ws = TestWorkspace :: new (
337- r#"{
338- "autoload": {
339- "psr-4": {
340- "Klarna\\": "src/Klarna/"
341- }
342- }
343- }"# ,
344- ) ;
345- ws. create_php_file (
346- "src/Klarna/Rest/Order.php" ,
347- "<?php\n namespace Klarna\\ Rest;\n class Order {}\n " ,
348- ) ;
349-
350- let mappings = parse_composer_json ( ws. root ( ) ) ;
351- let result = resolve_class_path ( & mappings, ws. root ( ) , "Klarna\\ Rest\\ Order" ) ;
352-
353- assert ! ( result. is_some( ) ) ;
354- let path = result. unwrap ( ) ;
355- assert ! ( path. ends_with( "src/Klarna/Rest/Order.php" ) ) ;
356- }
357-
358- #[ test]
359- fn test_resolve_strips_leading_backslash ( ) {
360- let ws = TestWorkspace :: new (
361- r#"{
362- "autoload": {
363- "psr-4": {
364- "Klarna\\": "src/Klarna/"
365- }
366- }
367- }"# ,
368- ) ;
369- ws. create_php_file (
370- "src/Klarna/Customer.php" ,
371- "<?php\n namespace Klarna;\n class Customer {}\n " ,
372- ) ;
373-
374- let mappings = parse_composer_json ( ws. root ( ) ) ;
375- let result = resolve_class_path ( & mappings, ws. root ( ) , "\\ Klarna\\ Customer" ) ;
376-
377- assert ! ( result. is_some( ) ) ;
378- }
379-
380- #[ test]
381- fn test_resolve_nonexistent_file_returns_none ( ) {
382- let ws = TestWorkspace :: new (
383- r#"{
384- "autoload": {
385- "psr-4": {
386- "Klarna\\": "src/Klarna/"
387- }
388- }
389- }"# ,
390- ) ;
391-
392- let mappings = parse_composer_json ( ws. root ( ) ) ;
393- let result = resolve_class_path ( & mappings, ws. root ( ) , "Klarna\\ DoesNotExist" ) ;
394-
395- assert ! ( result. is_none( ) ) ;
396- }
397-
398- #[ test]
399- fn test_resolve_no_matching_prefix ( ) {
400- let ws = TestWorkspace :: new (
401- r#"{
402- "autoload": {
403- "psr-4": {
404- "Klarna\\": "src/Klarna/"
405- }
406- }
407- }"# ,
408- ) ;
409-
410- let mappings = parse_composer_json ( ws. root ( ) ) ;
411- let result = resolve_class_path ( & mappings, ws. root ( ) , "Acme\\ Foo" ) ;
412-
413- assert ! ( result. is_none( ) ) ;
414- }
415-
416- #[ test]
417- fn test_resolve_longest_prefix_wins ( ) {
418- let ws = TestWorkspace :: new (
419- r#"{
420- "autoload": {
421- "psr-4": {
422- "Klarna\\": "src/Klarna/",
423- "Klarna\\Rest\\Tests\\": "tests/"
424- }
425- }
426- }"# ,
427- ) ;
428- ws. create_php_file (
429- "tests/OrderTest.php" ,
430- "<?php\n namespace Klarna\\ Rest\\ Tests;\n class OrderTest {}\n " ,
431- ) ;
432-
433- let mappings = parse_composer_json ( ws. root ( ) ) ;
434- let result = resolve_class_path ( & mappings, ws. root ( ) , "Klarna\\ Rest\\ Tests\\ OrderTest" ) ;
435-
436- assert ! ( result. is_some( ) ) ;
437- let path = result. unwrap ( ) ;
438- assert ! ( path. ends_with( "tests/OrderTest.php" ) ) ;
439- }
440-
441- #[ test]
442- fn test_resolve_builtin_types_return_none ( ) {
443- let ws = TestWorkspace :: new (
444- r#"{
445- "autoload": {
446- "psr-4": {
447- "": "src/"
448- }
449- }
450- }"# ,
451- ) ;
452-
453- let mappings = parse_composer_json ( ws. root ( ) ) ;
454-
455- for builtin in & [
456- "self" , "static" , "parent" , "string" , "int" , "float" , "bool" , "array" , "object" ,
457- "mixed" , "void" , "never" , "null" , "true" , "false" , "callable" , "iterable" ,
458- ] {
459- assert ! (
460- resolve_class_path( & mappings, ws. root( ) , builtin) . is_none( ) ,
461- "builtin type '{}' should not resolve" ,
462- builtin
463- ) ;
464- }
465- }
466-
467- #[ test]
468- fn test_resolve_array_paths_first_match ( ) {
469- let ws = TestWorkspace :: new (
470- r#"{
471- "autoload": {
472- "psr-4": {
473- "App\\": ["src/", "lib/"]
474- }
475- }
476- }"# ,
477- ) ;
478- // File exists only in lib/
479- ws. create_php_file (
480- "lib/Service.php" ,
481- "<?php\n namespace App;\n class Service {}\n " ,
482- ) ;
483-
484- let mappings = parse_composer_json ( ws. root ( ) ) ;
485- let result = resolve_class_path ( & mappings, ws. root ( ) , "App\\ Service" ) ;
486-
487- assert ! ( result. is_some( ) ) ;
488- let path = result. unwrap ( ) ;
489- assert ! ( path. ends_with( "lib/Service.php" ) ) ;
490- }
491-
492- #[ test]
493- fn test_normalise_path_adds_trailing_slash ( ) {
494- assert_eq ! ( normalise_path( "src" ) , "src/" ) ;
495- assert_eq ! ( normalise_path( "src/" ) , "src/" ) ;
496- assert_eq ! ( normalise_path( "" ) , "" ) ;
497- }
498-
499- #[ test]
500- fn test_normalise_path_converts_backslashes ( ) {
501- assert_eq ! ( normalise_path( "src\\ Klarna\\ " ) , "src/Klarna/" ) ;
502- }
503-
504- #[ test]
505- fn test_prefix_without_trailing_backslash ( ) {
506- let ws = TestWorkspace :: new (
507- r#"{
508- "autoload": {
509- "psr-4": {
510- "App": "src/"
511- }
512- }
513- }"# ,
514- ) ;
515- ws. create_php_file (
516- "src/Service.php" ,
517- "<?php\n namespace App;\n class Service {}\n " ,
518- ) ;
519-
520- let mappings = parse_composer_json ( ws. root ( ) ) ;
521- // The prefix gets normalised to "App\"
522- assert_eq ! ( mappings[ 0 ] . prefix, "App\\ " ) ;
523-
524- let result = resolve_class_path ( & mappings, ws. root ( ) , "App\\ Service" ) ;
525- assert ! ( result. is_some( ) ) ;
526- }
527- }
0 commit comments