@@ -22,10 +22,15 @@ import {
2222 InvalidRequestParameterException ,
2323 ResourceNotFoundException ,
2424} from "src/exception" ;
25- import { findTilesRepoSchema } from "./capital-project.repository.schema" ;
25+ import {
26+ capitalProjectCsvRepoSchema ,
27+ findTilesRepoSchema ,
28+ findCsvRepoSchema ,
29+ } from "./capital-project.repository.schema" ;
2630import { BoroughRepository } from "src/borough/borough.repository" ;
2731import { SpatialRepositoryMock } from "test/spatial/spatial.repository.mock" ;
2832import { SpatialRepository } from "src/spatial/spatial.repository" ;
33+ import { SpatialService } from "src/spatial/spatial.service" ;
2934
3035describe ( "CapitalProjectService" , ( ) => {
3136 let capitalProjectService : CapitalProjectService ;
@@ -56,6 +61,7 @@ describe("CapitalProjectService", () => {
5661 AgencyBudgetRepository ,
5762 BoroughRepository ,
5863 SpatialRepository ,
64+ SpatialService ,
5965 ] ,
6066 } )
6167 . overrideProvider ( CapitalProjectRepository )
@@ -667,4 +673,366 @@ describe("CapitalProjectService", () => {
667673 ) . rejects . toThrow ( ResourceNotFoundException ) ;
668674 } ) ;
669675 } ) ;
676+
677+ describe ( "findCsv" , ( ) => {
678+ it ( "should return a list of capital projects for download" , async ( ) => {
679+ const csv = await capitalProjectService . findCsv ( { } ) ;
680+ expect ( ( ) => findCsvRepoSchema . parse ( csv ) ) . not . toThrow ( ) ;
681+ expect ( csv . length ) . toBe ( 9 ) ;
682+ const firstItem = csv [ 0 ] ;
683+ expect ( ( ) =>
684+ capitalProjectCsvRepoSchema . strict ( ) . parse ( firstItem ) ,
685+ ) . not . toThrow ( ) ;
686+ } ) ;
687+
688+ it ( "should return a list of capital projects filtered by borough ids" , async ( ) => {
689+ const { id } = boroughRepositoryMock . boroughs [ 0 ] ;
690+
691+ const csv = await capitalProjectService . findCsv ( {
692+ boroughIds : [ id ] ,
693+ } ) ;
694+
695+ expect ( ( ) => findCsvRepoSchema . parse ( csv ) ) . not . toThrow ( ) ;
696+ expect ( csv . length ) . toBe ( 5 ) ;
697+ const firstItem = csv [ 0 ] ;
698+ expect ( ( ) =>
699+ capitalProjectCsvRepoSchema . strict ( ) . parse ( firstItem ) ,
700+ ) . not . toThrow ( ) ;
701+ } ) ;
702+
703+ it ( "should still return a list of capital projects when passing borough duplicate ids" , async ( ) => {
704+ const { id } = boroughRepositoryMock . boroughs [ 0 ] ;
705+
706+ const csv = await capitalProjectService . findCsv ( {
707+ boroughIds : [ id , id ] ,
708+ } ) ;
709+
710+ expect ( ( ) => findCsvRepoSchema . parse ( csv ) ) . not . toThrow ( ) ;
711+ expect ( csv . length ) . toBe ( 5 ) ;
712+ const firstItem = csv [ 0 ] ;
713+ expect ( ( ) =>
714+ capitalProjectCsvRepoSchema . strict ( ) . parse ( firstItem ) ,
715+ ) . not . toThrow ( ) ;
716+ } ) ;
717+
718+ it ( "should return an InvalidRequestParameterException when a provided borough id cannot be found" , async ( ) => {
719+ const id = "6" ;
720+
721+ expect (
722+ capitalProjectService . findCsv ( {
723+ boroughIds : [ id ] ,
724+ } ) ,
725+ ) . rejects . toThrow ( InvalidRequestParameterException ) ;
726+ } ) ;
727+
728+ it ( "should return a list of capital projects for download filtered by community district" , async ( ) => {
729+ const communityDistrict = communityDistrictRepositoryMock . districts [ 0 ] ;
730+ const csv = await capitalProjectService . findCsv ( {
731+ communityDistrictCombinedId : `${ communityDistrict . boroughId } ${ communityDistrict . id } ` ,
732+ } ) ;
733+
734+ expect ( ( ) => findCsvRepoSchema . parse ( csv ) ) . not . toThrow ( ) ;
735+ expect ( csv . length ) . toBe ( 2 ) ;
736+ const firstItem = csv [ 0 ] ;
737+ expect ( ( ) =>
738+ capitalProjectCsvRepoSchema . strict ( ) . parse ( firstItem ) ,
739+ ) . not . toThrow ( ) ;
740+ } ) ;
741+
742+ it ( "should return a InvalidRequestParameterException error when a community district with the given id cannot be found" , async ( ) => {
743+ const id = "999" ;
744+
745+ expect (
746+ capitalProjectService . findCsv ( {
747+ communityDistrictCombinedId : id ,
748+ } ) ,
749+ ) . rejects . toThrow ( InvalidRequestParameterException ) ;
750+ } ) ;
751+
752+ it ( "should return a list of capital projects for download filtered by community districts" , async ( ) => {
753+ const csv = await capitalProjectService . findCsv ( {
754+ communityDistrictCombinedIds : [
755+ `${ communityDistrictRepositoryMock . districts [ 0 ] . boroughId } ${ communityDistrictRepositoryMock . districts [ 0 ] . id } ` ,
756+ `${ communityDistrictRepositoryMock . districts [ 1 ] . boroughId } ${ communityDistrictRepositoryMock . districts [ 1 ] . id } ` ,
757+ ] ,
758+ } ) ;
759+
760+ expect ( ( ) => findCsvRepoSchema . parse ( csv ) ) . not . toThrow ( ) ;
761+ expect ( csv . length ) . toBe ( 9 ) ;
762+ const firstItem = csv [ 0 ] ;
763+ expect ( ( ) =>
764+ capitalProjectCsvRepoSchema . strict ( ) . parse ( firstItem ) ,
765+ ) . not . toThrow ( ) ;
766+ } ) ;
767+
768+ it ( "should return a list of capital projects for download filtered by city council district" , async ( ) => {
769+ const cityCouncilDistrictId =
770+ cityCouncilDistrictRepositoryMock . districts [ 0 ] . id ;
771+ const csv = await capitalProjectService . findCsv ( {
772+ cityCouncilDistrictId,
773+ } ) ;
774+
775+ expect ( ( ) => findCsvRepoSchema . parse ( csv ) ) . not . toThrow ( ) ;
776+ expect ( csv . length ) . toBe ( 5 ) ;
777+ const firstItem = csv [ 0 ] ;
778+ expect ( ( ) =>
779+ capitalProjectCsvRepoSchema . strict ( ) . parse ( firstItem ) ,
780+ ) . not . toThrow ( ) ;
781+ } ) ;
782+
783+ it ( "should return a InvalidRequestParameterException error when a city council district with the given id cannot be found" , async ( ) => {
784+ const id = "60" ;
785+
786+ expect (
787+ capitalProjectService . findCsv ( {
788+ cityCouncilDistrictId : id ,
789+ } ) ,
790+ ) . rejects . toThrow ( InvalidRequestParameterException ) ;
791+ } ) ;
792+
793+ it ( "should return a list of capital projects for download filtered by city council districts" , async ( ) => {
794+ const csv = await capitalProjectService . findCsv ( {
795+ cityCouncilDistrictIds : [
796+ cityCouncilDistrictRepositoryMock . districts [ 0 ] . id ,
797+ cityCouncilDistrictRepositoryMock . districts [ 1 ] . id ,
798+ ] ,
799+ } ) ;
800+
801+ expect ( ( ) => findCsvRepoSchema . parse ( csv ) ) . not . toThrow ( ) ;
802+ expect ( csv . length ) . toBe ( 9 ) ;
803+ const firstItem = csv [ 0 ] ;
804+ expect ( ( ) =>
805+ capitalProjectCsvRepoSchema . strict ( ) . parse ( firstItem ) ,
806+ ) . not . toThrow ( ) ;
807+ } ) ;
808+
809+ it ( "should filter by an agency budget code" , async ( ) => {
810+ const agencyBudget =
811+ capitalProjectRepository . agencyBudgetRepositoryMock . agencyBudgets [ 1 ]
812+ . code ;
813+ const csv = await capitalProjectService . findCsv ( {
814+ agencyBudget : agencyBudget ,
815+ } ) ;
816+ expect ( ( ) => findCsvRepoSchema . parse ( csv ) ) . not . toThrow ( ) ;
817+
818+ expect ( csv . length ) . toBe ( 7 ) ;
819+ const firstItem = csv [ 0 ] ;
820+ expect ( ( ) =>
821+ capitalProjectCsvRepoSchema . strict ( ) . parse ( firstItem ) ,
822+ ) . not . toThrow ( ) ;
823+ } ) ;
824+
825+ it ( "should filter by geometry" , async ( ) => {
826+ const geometry = "Point" ;
827+ const lons = [ - 74.010521 ] ;
828+ const lats = [ 40.708219 ] ;
829+
830+ const csv = await capitalProjectService . findCsv ( {
831+ geometry,
832+ lons,
833+ lats,
834+ } ) ;
835+ expect ( ( ) => findCsvRepoSchema . parse ( csv ) ) . not . toThrow ( ) ;
836+
837+ expect ( csv . length ) . toBe ( 0 ) ;
838+ } ) ;
839+
840+ it ( "should filter by geometry and buffer" , async ( ) => {
841+ const geometry = "Point" ;
842+ const lons = [ - 74.010521 ] ;
843+ const lats = [ 40.708219 ] ;
844+ const buffer = 1e6 ;
845+
846+ const csv = await capitalProjectService . findCsv ( {
847+ geometry,
848+ lons,
849+ lats,
850+ buffer,
851+ } ) ;
852+ expect ( ( ) => findCsvRepoSchema . parse ( csv ) ) . not . toThrow ( ) ;
853+
854+ expect ( csv . length ) . toBe ( 3 ) ;
855+ const firstItem = csv [ 0 ] ;
856+ expect ( ( ) =>
857+ capitalProjectCsvRepoSchema . strict ( ) . parse ( firstItem ) ,
858+ ) . not . toThrow ( ) ;
859+ } ) ;
860+
861+ it ( "should return an InvalidRequestParameterException a geometry is provided without coordinates" , async ( ) => {
862+ const geometry = "Point" ;
863+
864+ expect (
865+ capitalProjectService . findCsv ( {
866+ geometry,
867+ } ) ,
868+ ) . rejects . toThrow ( InvalidRequestParameterException ) ;
869+ } ) ;
870+
871+ it ( "should return an InvalidRequestParameterException when coordinates are provided without a geometry" , async ( ) => {
872+ const lons = [ - 74.010521 ] ;
873+ const lats = [ 40.708219 ] ;
874+
875+ expect (
876+ capitalProjectService . findCsv ( {
877+ lons,
878+ lats,
879+ } ) ,
880+ ) . rejects . toThrow ( InvalidRequestParameterException ) ;
881+ } ) ;
882+
883+ it ( "should return an InvalidRequestParameterException when a buffer is provided without a geometry" , async ( ) => {
884+ const buffer = 1e6 ;
885+
886+ expect (
887+ capitalProjectService . findCsv ( {
888+ buffer,
889+ } ) ,
890+ ) . rejects . toThrow ( InvalidRequestParameterException ) ;
891+ } ) ;
892+
893+ it ( "should return an InvalidRequestParameterException when the lon and lat lengths differ" , async ( ) => {
894+ const geometry = "Point" ;
895+ const lons = [ - 74.010521 , - 74.010521 ] ;
896+ const lats = [ 40.708219 ] ;
897+
898+ expect (
899+ capitalProjectService . findCsv ( {
900+ geometry,
901+ lons,
902+ lats,
903+ } ) ,
904+ ) . rejects . toThrow ( InvalidRequestParameterException ) ;
905+ } ) ;
906+
907+ it ( "should return a list of capital projects for download by managing agency" , async ( ) => {
908+ const { initials } = capitalProjectRepository . agencyRepoMock . agencies [ 0 ] ;
909+ const csv = await capitalProjectService . findCsv ( {
910+ managingAgency : initials ,
911+ } ) ;
912+
913+ expect ( ( ) => findCsvRepoSchema . parse ( csv ) ) . not . toThrow ( ) ;
914+
915+ expect ( csv . length ) . toBe ( 2 ) ;
916+ const firstItem = csv [ 0 ] ;
917+ expect ( ( ) =>
918+ capitalProjectCsvRepoSchema . strict ( ) . parse ( firstItem ) ,
919+ ) . not . toThrow ( ) ;
920+ } ) ;
921+
922+ it ( "should return a InvalidRequestParameterException error when a managing agency with the given id cannot be found" , async ( ) => {
923+ const managingAgency = "DNE" ;
924+
925+ expect (
926+ capitalProjectService . findCsv ( {
927+ managingAgency,
928+ } ) ,
929+ ) . rejects . toThrow ( InvalidRequestParameterException ) ;
930+ } ) ;
931+
932+ it ( "should return a list of capital projects for download with total commitments above the minimum" , async ( ) => {
933+ const commitmentsTotalMin = "0" ;
934+ const csv = await capitalProjectService . findCsv ( {
935+ commitmentsTotalMin,
936+ } ) ;
937+
938+ expect ( ( ) => findCsvRepoSchema . parse ( csv ) ) . not . toThrow ( ) ;
939+
940+ expect ( csv . length ) . toBe ( 9 ) ;
941+ const firstItem = csv [ 0 ] ;
942+ expect ( ( ) =>
943+ capitalProjectCsvRepoSchema . strict ( ) . parse ( firstItem ) ,
944+ ) . not . toThrow ( ) ;
945+ } ) ;
946+
947+ it ( "should return a list of capital projects for download with total commitments below the maximum" , async ( ) => {
948+ const commitmentsTotalMax = "10000000000" ;
949+ const csv = await capitalProjectService . findCsv ( {
950+ commitmentsTotalMax,
951+ } ) ;
952+
953+ expect ( ( ) => findCsvRepoSchema . parse ( csv ) ) . not . toThrow ( ) ;
954+
955+ expect ( csv . length ) . toBe ( 9 ) ;
956+ const firstItem = csv [ 0 ] ;
957+ expect ( ( ) =>
958+ capitalProjectCsvRepoSchema . strict ( ) . parse ( firstItem ) ,
959+ ) . not . toThrow ( ) ;
960+ } ) ;
961+
962+ it ( "should return a InvalidRequestParameterException error when the maximum total commitments is less than the minimum" , async ( ) => {
963+ const commitmentsTotalMin = "10000000000" ;
964+ const commitmentsTotalMax = "0" ;
965+
966+ expect (
967+ capitalProjectService . findCsv ( {
968+ commitmentsTotalMin,
969+ commitmentsTotalMax,
970+ } ) ,
971+ ) . rejects . toThrow ( InvalidRequestParameterException ) ;
972+ } ) ;
973+
974+ it ( "should throw an error when requesting an agency budget that does not exist" , async ( ) => {
975+ const missingAgencyBudget = "hr" ;
976+
977+ expect ( ( ) =>
978+ capitalProjectService . findCsv ( {
979+ agencyBudget : missingAgencyBudget ,
980+ } ) ,
981+ ) . rejects . toThrow ( InvalidRequestParameterException ) ;
982+ } ) ;
983+
984+ it ( "should return only capital projects with non-null geometries when isMapped is true" , async ( ) => {
985+ const csv = await capitalProjectService . findCsv ( {
986+ isMapped : true ,
987+ } ) ;
988+ expect ( ( ) => findCsvRepoSchema . parse ( csv ) ) . not . toThrow ( ) ;
989+
990+ expect ( csv . length ) . toBe ( 4 ) ;
991+ const firstItem = csv [ 0 ] ;
992+ expect ( ( ) =>
993+ capitalProjectCsvRepoSchema . strict ( ) . parse ( firstItem ) ,
994+ ) . not . toThrow ( ) ;
995+ } ) ;
996+
997+ it ( "should return only capital projects with null geometries when isMapped is false" , async ( ) => {
998+ const csv = await capitalProjectService . findCsv ( {
999+ isMapped : false ,
1000+ } ) ;
1001+ expect ( ( ) => findCsvRepoSchema . parse ( csv ) ) . not . toThrow ( ) ;
1002+
1003+ expect ( csv . length ) . toBe ( 5 ) ;
1004+ const firstItem = csv [ 0 ] ;
1005+ expect ( ( ) =>
1006+ capitalProjectCsvRepoSchema . strict ( ) . parse ( firstItem ) ,
1007+ ) . not . toThrow ( ) ;
1008+ } ) ;
1009+
1010+ it ( "should return an InvalidRequestParameterException when both boroughIds and isMapped are provided" , async ( ) => {
1011+ const { id } = boroughRepositoryMock . boroughs [ 0 ] ;
1012+ expect (
1013+ capitalProjectService . findCsv ( {
1014+ boroughIds : [ id ] ,
1015+ isMapped : true ,
1016+ } ) ,
1017+ ) . rejects . toThrow ( InvalidRequestParameterException ) ;
1018+ } ) ;
1019+
1020+ it ( "should return a InvalidRequestParameterException error when both a city council district id and isMapped are provided" , async ( ) => {
1021+ expect (
1022+ capitalProjectService . findCsv ( {
1023+ cityCouncilDistrictId : "50" ,
1024+ isMapped : true ,
1025+ } ) ,
1026+ ) . rejects . toThrow ( InvalidRequestParameterException ) ;
1027+ } ) ;
1028+
1029+ it ( "should return a InvalidRequestParameterException error when a borough id, a community district id, and isMapped are provided" , async ( ) => {
1030+ expect (
1031+ capitalProjectService . findCsv ( {
1032+ communityDistrictCombinedId : "101" ,
1033+ isMapped : true ,
1034+ } ) ,
1035+ ) . rejects . toThrow ( InvalidRequestParameterException ) ;
1036+ } ) ;
1037+ } ) ;
6701038} ) ;
0 commit comments