@@ -23,6 +23,7 @@ import {
2323 it ,
2424 vi ,
2525} from "vitest"
26+ import { z } from "zod"
2627
2728import { getRegistry , getRegistryItems } from "./api"
2829
@@ -718,4 +719,287 @@ describe("getRegistry", () => {
718719 RegistryFetchError
719720 )
720721 } )
722+
723+ it ( "should throw RegistryNotConfiguredError when registry is not in config" , async ( ) => {
724+ const mockConfig = {
725+ style : "new-york" ,
726+ tailwind : { baseColor : "neutral" , cssVariables : true } ,
727+ } as any
728+
729+ await expect ( getRegistry ( "@nonexistent" , mockConfig ) ) . rejects . toThrow (
730+ RegistryNotConfiguredError
731+ )
732+ } )
733+
734+ it ( "should handle registry with no items gracefully" , async ( ) => {
735+ const registryData = {
736+ name : "@empty/registry" ,
737+ homepage : "https://empty.com" ,
738+ items : [ ] ,
739+ }
740+
741+ server . use (
742+ http . get ( "https://empty.com/registry.json" , ( ) => {
743+ return HttpResponse . json ( registryData )
744+ } )
745+ )
746+
747+ const mockConfig = {
748+ style : "new-york" ,
749+ tailwind : { baseColor : "neutral" , cssVariables : true } ,
750+ registries : {
751+ "@empty" : {
752+ url : "https://empty.com/{name}.json" ,
753+ } ,
754+ } ,
755+ } as any
756+
757+ const result = await getRegistry ( "@empty" , mockConfig )
758+ expect ( result ) . toMatchObject ( registryData )
759+ expect ( result . items ) . toHaveLength ( 0 )
760+ } )
761+
762+ it ( "should handle 404 error from registry endpoint" , async ( ) => {
763+ server . use (
764+ http . get ( "https://notfound.com/registry.json" , ( ) => {
765+ return HttpResponse . json ( { error : "Not Found" } , { status : 404 } )
766+ } )
767+ )
768+
769+ const mockConfig = {
770+ style : "new-york" ,
771+ tailwind : { baseColor : "neutral" , cssVariables : true } ,
772+ registries : {
773+ "@notfound" : {
774+ url : "https://notfound.com/{name}.json" ,
775+ } ,
776+ } ,
777+ } as any
778+
779+ await expect ( getRegistry ( "@notfound" , mockConfig ) ) . rejects . toThrow (
780+ RegistryNotFoundError
781+ )
782+ } )
783+
784+ it ( "should handle 401 error from registry endpoint" , async ( ) => {
785+ server . use (
786+ http . get ( "https://unauthorized.com/registry.json" , ( ) => {
787+ return HttpResponse . json ( { error : "Unauthorized" } , { status : 401 } )
788+ } )
789+ )
790+
791+ const mockConfig = {
792+ style : "new-york" ,
793+ tailwind : { baseColor : "neutral" , cssVariables : true } ,
794+ registries : {
795+ "@unauthorized" : {
796+ url : "https://unauthorized.com/{name}.json" ,
797+ } ,
798+ } ,
799+ } as any
800+
801+ await expect ( getRegistry ( "@unauthorized" , mockConfig ) ) . rejects . toThrow (
802+ RegistryUnauthorizedError
803+ )
804+ } )
805+
806+ it ( "should handle 403 error from registry endpoint" , async ( ) => {
807+ server . use (
808+ http . get ( "https://forbidden.com/registry.json" , ( ) => {
809+ return HttpResponse . json ( { error : "Forbidden" } , { status : 403 } )
810+ } )
811+ )
812+
813+ const mockConfig = {
814+ style : "new-york" ,
815+ tailwind : { baseColor : "neutral" , cssVariables : true } ,
816+ registries : {
817+ "@forbidden" : {
818+ url : "https://forbidden.com/{name}.json" ,
819+ } ,
820+ } ,
821+ } as any
822+
823+ await expect ( getRegistry ( "@forbidden" , mockConfig ) ) . rejects . toThrow (
824+ RegistryForbiddenError
825+ )
826+ } )
827+
828+ it ( "should set headers in context when provided" , async ( ) => {
829+ const registryData = {
830+ name : "@headers-test/registry" ,
831+ homepage : "https://headers.com" ,
832+ items : [ ] ,
833+ }
834+
835+ let receivedHeaders : Record < string , string > = { }
836+ server . use (
837+ http . get ( "https://headers.com/registry.json" , ( { request } ) => {
838+ request . headers . forEach ( ( value , key ) => {
839+ receivedHeaders [ key ] = value
840+ } )
841+ return HttpResponse . json ( registryData )
842+ } )
843+ )
844+
845+ const mockConfig = {
846+ style : "new-york" ,
847+ tailwind : { baseColor : "neutral" , cssVariables : true } ,
848+ registries : {
849+ "@headers-test" : {
850+ url : "https://headers.com/{name}.json" ,
851+ headers : {
852+ "X-Custom-Header" : "test-value" ,
853+ Authorization : "Bearer test-token" ,
854+ } ,
855+ } ,
856+ } ,
857+ } as any
858+
859+ await getRegistry ( "@headers-test" , mockConfig )
860+
861+ expect ( receivedHeaders [ "x-custom-header" ] ) . toBe ( "test-value" )
862+ expect ( receivedHeaders . authorization ) . toBe ( "Bearer test-token" )
863+ } )
864+
865+ it ( "should not set headers in context when none provided" , async ( ) => {
866+ const registryData = {
867+ name : "@no-headers/registry" ,
868+ homepage : "https://noheaders.com" ,
869+ items : [ ] ,
870+ }
871+
872+ server . use (
873+ http . get ( "https://noheaders.com/registry.json" , ( ) => {
874+ return HttpResponse . json ( registryData )
875+ } )
876+ )
877+
878+ const mockConfig = {
879+ style : "new-york" ,
880+ tailwind : { baseColor : "neutral" , cssVariables : true } ,
881+ registries : {
882+ "@no-headers" : {
883+ url : "https://noheaders.com/{name}.json" ,
884+ } ,
885+ } ,
886+ } as any
887+
888+ const result = await getRegistry ( "@no-headers" , mockConfig )
889+ expect ( result ) . toMatchObject ( registryData )
890+ } )
891+
892+ it ( "should handle registry items with slashes" , async ( ) => {
893+ const registryData = {
894+ name : "@acme/registry" ,
895+ homepage : "https://acme.com" ,
896+ items : [ ] ,
897+ }
898+
899+ server . use (
900+ http . get ( "https://acme.com/sub/registry.json" , ( ) => {
901+ return HttpResponse . json ( registryData )
902+ } )
903+ )
904+
905+ const mockConfig = {
906+ style : "new-york" ,
907+ tailwind : { baseColor : "neutral" , cssVariables : true } ,
908+ registries : {
909+ "@acme" : {
910+ url : "https://acme.com/{name}.json" ,
911+ } ,
912+ } ,
913+ } as any
914+
915+ const result = await getRegistry ( "@acme/sub" , mockConfig )
916+ expect ( result ) . toMatchObject ( registryData )
917+ } )
918+
919+ it ( "should use configWithDefaults to fill missing config values" , async ( ) => {
920+ const registryData = {
921+ name : "@defaults/registry" ,
922+ homepage : "https://defaults.com" ,
923+ items : [ ] ,
924+ }
925+
926+ server . use (
927+ http . get ( "https://defaults.com/registry.json" , ( ) => {
928+ return HttpResponse . json ( registryData )
929+ } )
930+ )
931+
932+ const minimalConfig = {
933+ registries : {
934+ "@defaults" : {
935+ url : "https://defaults.com/{name}.json" ,
936+ } ,
937+ } ,
938+ } as any
939+
940+ const result = await getRegistry ( "@defaults" , minimalConfig )
941+ expect ( result ) . toMatchObject ( registryData )
942+ } )
943+
944+ it ( "should handle malformed JSON response" , async ( ) => {
945+ server . use (
946+ http . get ( "https://malformed.com/registry.json" , ( ) => {
947+ return new Response ( "{ malformed json }" , {
948+ status : 200 ,
949+ headers : { "Content-Type" : "application/json" } ,
950+ } )
951+ } )
952+ )
953+
954+ const mockConfig = {
955+ style : "new-york" ,
956+ tailwind : { baseColor : "neutral" , cssVariables : true } ,
957+ registries : {
958+ "@malformed" : {
959+ url : "https://malformed.com/{name}.json" ,
960+ } ,
961+ } ,
962+ } as any
963+
964+ await expect ( getRegistry ( "@malformed" , mockConfig ) ) . rejects . toThrow ( )
965+ } )
966+
967+ it ( "should throw RegistryParseError with proper context" , async ( ) => {
968+ const invalidData = {
969+ homepage : "https://invalid.com" ,
970+ items : "not-an-array" ,
971+ }
972+
973+ server . use (
974+ http . get ( "https://parsetest.com/registry.json" , ( ) => {
975+ return HttpResponse . json ( invalidData )
976+ } )
977+ )
978+
979+ const mockConfig = {
980+ style : "new-york" ,
981+ tailwind : { baseColor : "neutral" , cssVariables : true } ,
982+ registries : {
983+ "@parsetest" : {
984+ url : "https://parsetest.com/{name}.json" ,
985+ } ,
986+ } ,
987+ } as any
988+
989+ try {
990+ await getRegistry ( "@parsetest/registry" , mockConfig )
991+ expect . fail ( "Should have thrown RegistryParseError" )
992+ } catch ( error ) {
993+ expect ( error ) . toBeInstanceOf ( RegistryParseError )
994+ if ( error instanceof RegistryParseError ) {
995+ expect ( error . message ) . toContain ( "Failed to parse registry" )
996+ expect ( error . message ) . toContain ( "@parsetest/registry" )
997+ expect ( error . context ?. item ) . toBe ( "@parsetest/registry" )
998+ expect ( error . parseError ) . toBeDefined ( )
999+ if ( error . parseError instanceof z . ZodError ) {
1000+ expect ( error . parseError . errors . length ) . toBeGreaterThan ( 0 )
1001+ }
1002+ }
1003+ }
1004+ } )
7211005} )
0 commit comments