diff --git a/.github/actions/setup-node-pnpm/action.yml b/.github/actions/setup-node-pnpm/action.yml new file mode 100644 index 0000000..35abb2c --- /dev/null +++ b/.github/actions/setup-node-pnpm/action.yml @@ -0,0 +1,17 @@ +name: Setup Node and PNPM +description: Setup node and pnpm +runs: + using: "composite" + steps: + - uses: pnpm/action-setup@v4 + with: + version: 10.11.1 + + - uses: actions/setup-node@v4 + with: + node-version: 22.16.0 + cache: "pnpm" + + - name: Installation of project dependencies + shell: bash + run: pnpm install --frozen-lockfile diff --git a/.github/workflows/tests.yml b/.github/workflows/run-e2e-test.yml similarity index 56% rename from .github/workflows/tests.yml rename to .github/workflows/run-e2e-test.yml index bae9d5e..33a1141 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/run-e2e-test.yml @@ -1,4 +1,4 @@ -name: Tests +name: Tests e2e on: pull_request: @@ -16,34 +16,24 @@ jobs: postgres: image: bitnami/postgresql ports: - - 5432:5432 + - "5432:5432" env: - POSTGRESQL_USERNAME: docker - POSTGRESQL_PASSWORD: docker + POSTGRES_USER: docker + POSTGRES_PASSWORD: docker POSTGRES_DB: api-findAFriend steps: - name: Checkout Code uses: actions/checkout@v4 - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version: 22.16.0 - - - name: Install pnpm - uses: pnpm/action-setup@v4 - with: - version: 10.11.1 - run_install: false - - - name: Install dependencies - run: pnpm install --frozen-lockfile + - name: Setup Node and PNPM + uses: ./.github/actions/setup-node-pnpm - name: Prisma Generate run: pnpm db:generate - name: Run tests - run: pnpm test + run: pnpm test:e2e env: + JWT_SECRET: testing DATABASE_URL: "postgresql://docker:docker@localhost:5432/api-findAFriend?schema=public" diff --git a/.github/workflows/run-unit-test.yml b/.github/workflows/run-unit-test.yml new file mode 100644 index 0000000..3dd352e --- /dev/null +++ b/.github/workflows/run-unit-test.yml @@ -0,0 +1,20 @@ +name: Tests unit + +on: [push] + +jobs: + run-ci: + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Setup Node ande PNPM + uses: ./.github/actions/setup-node-pnpm + + - name: Runing prisma generate + run: pnpm db:generate + + - name: Run tests + run: pnpm test:unit diff --git a/package.json b/package.json index 0eafe16..3fe14eb 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,10 @@ "check": "biome check", "format": "biome format", "lint": "biome lint", - "test": "vitest run", - "test:watch": "vitest", + "test:unit": "vitest run --project unit", + "test:unit:watch": "vitest --project unit", + "test:e2e": "vitest run --project e2e", + "test:e2e:watch": "vitest --project e2e", "test:coverage": "vitest run --coverage", "test:ui": "vitest --ui", "db:generate": "prisma generate", @@ -35,12 +37,16 @@ "@biomejs/biome": "2.1.1", "@faker-js/faker": "^9.9.0", "@types/node": "^24.0.4", + "@types/supertest": "^6.0.3", "@vitest/coverage-v8": "3.2.4", "@vitest/ui": "3.2.4", + "dotenv": "^17.2.0", "prisma": "^6.10.1", + "supertest": "^7.1.3", "tsup": "^8.5.0", "tsx": "^4.20.3", "typescript": "^5.8.3", + "vite": "^7.0.4", "vite-tsconfig-paths": "^5.1.4", "vitest": "^3.2.4" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 886c991..03302df 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,15 +42,24 @@ importers: '@types/node': specifier: ^24.0.4 version: 24.0.4 + '@types/supertest': + specifier: ^6.0.3 + version: 6.0.3 '@vitest/coverage-v8': specifier: 3.2.4 version: 3.2.4(vitest@3.2.4) '@vitest/ui': specifier: 3.2.4 version: 3.2.4(vitest@3.2.4) + dotenv: + specifier: ^17.2.0 + version: 17.2.0 prisma: specifier: ^6.10.1 version: 6.10.1(typescript@5.8.3) + supertest: + specifier: ^7.1.3 + version: 7.1.3 tsup: specifier: ^8.5.0 version: 8.5.0(jiti@2.4.2)(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0) @@ -60,9 +69,12 @@ importers: typescript: specifier: ^5.8.3 version: 5.8.3 + vite: + specifier: ^7.0.4 + version: 7.0.4(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.8.3)(vite@7.0.1(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0)) + version: 5.1.4(typescript@5.8.3)(vite@7.0.4(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0)) vitest: specifier: ^3.2.4 version: 3.2.4(@types/node@24.0.4)(@vitest/ui@3.2.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0) @@ -358,6 +370,13 @@ packages: resolution: {integrity: sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==} engines: {node: '>=8'} + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@paralleldrive/cuid2@2.2.2': + resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -502,15 +521,27 @@ packages: '@types/chai@5.2.2': resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/cookiejar@2.1.5': + resolution: {integrity: sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==} + '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/methods@1.1.4': + resolution: {integrity: sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==} + '@types/node@24.0.4': resolution: {integrity: sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA==} + '@types/superagent@8.1.9': + resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==} + + '@types/supertest@6.0.3': + resolution: {integrity: sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==} + '@vitest/coverage-v8@3.2.4': resolution: {integrity: sha512-EyF9SXU6kS5Ku/U82E259WSnvg6c8KTjppUncuNdm5QHpe17mwREHnjDzozC8x9MZ0xfBUFSaLkRv4TMA75ALQ==} peerDependencies: @@ -592,6 +623,9 @@ packages: any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + asap@2.0.6: + resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} + asn1.js@5.4.1: resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} @@ -602,6 +636,9 @@ packages: ast-v8-to-istanbul@0.3.3: resolution: {integrity: sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + atomic-sleep@1.0.0: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} @@ -632,6 +669,14 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + chai@5.2.0: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} @@ -651,10 +696,17 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} + component-emitter@1.3.1: + resolution: {integrity: sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==} + confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} @@ -666,6 +718,9 @@ packages: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} + cookiejar@2.1.4: + resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -683,10 +738,25 @@ packages: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} + dezalgo@1.0.4: + resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} + + dotenv@17.2.0: + resolution: {integrity: sha512-Q4sgBT60gzd0BB0lSyYD3xM4YxrXA9y4uBDof1JNYGzOXrQdQ6yX+7XIAqoFOGQFOTK1D3Hts5OllpxMDZFONQ==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -699,9 +769,25 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + esbuild@0.25.5: resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} engines: {node: '>=18'} @@ -734,6 +820,9 @@ packages: resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} engines: {node: '>=6'} + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fast-uri@3.0.6: resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} @@ -781,11 +870,30 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + form-data@4.0.3: + resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} + engines: {node: '>= 6'} + + formidable@3.5.4: + resolution: {integrity: sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==} + engines: {node: '>=14.0.0'} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + get-tsconfig@4.10.1: resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==} @@ -796,10 +904,26 @@ packages: globrex@0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -886,6 +1010,27 @@ packages: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} @@ -922,6 +1067,10 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + obliterator@2.0.5: resolution: {integrity: sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==} @@ -929,6 +1078,9 @@ packages: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -1013,6 +1165,10 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} @@ -1083,6 +1239,22 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -1142,6 +1314,14 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true + superagent@10.2.2: + resolution: {integrity: sha512-vWMq11OwWCC84pQaFPzF/VO3BrjkCeewuvJgt1jfV0499Z1QSAWN4EqfMM5WlFDDX9/oP8JjlDKpblrmEoyu4Q==} + engines: {node: '>=14.18.0'} + + supertest@7.1.3: + resolution: {integrity: sha512-ORY0gPa6ojmg/C74P/bDoS21WL6FMXq5I8mawkEz30/zkwdu0gOeqstFy316vHG6OKxqQ+IbGneRemHI8WraEw==} + engines: {node: '>=14.18.0'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -1258,8 +1438,8 @@ packages: vite: optional: true - vite@7.0.1: - resolution: {integrity: sha512-BiKOQoW5HGR30E6JDeNsati6HnSPMVEKbkIWbCiol+xKeu3g5owrjy7kbk/QEMuzCV87dSUTvycYKmlcfGKq3Q==} + vite@7.0.4: + resolution: {integrity: sha512-SkaSguuS7nnmV7mfJ8l81JGBFV7Gvzp8IzgE8A8t23+AxuNX61Q5H1Tpz5efduSN7NHC8nQXD3sKQKZAu5mNEA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -1350,6 +1530,9 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -1567,6 +1750,12 @@ snapshots: '@lukeed/ms@2.0.2': {} + '@noble/hashes@1.8.0': {} + + '@paralleldrive/cuid2@2.2.2': + dependencies: + '@noble/hashes': 1.8.0 + '@pkgjs/parseargs@0.11.0': optional: true @@ -1670,14 +1859,30 @@ snapshots: dependencies: '@types/deep-eql': 4.0.2 + '@types/cookiejar@2.1.5': {} + '@types/deep-eql@4.0.2': {} '@types/estree@1.0.8': {} + '@types/methods@1.1.4': {} + '@types/node@24.0.4': dependencies: undici-types: 7.8.0 + '@types/superagent@8.1.9': + dependencies: + '@types/cookiejar': 2.1.5 + '@types/methods': 1.1.4 + '@types/node': 24.0.4 + form-data: 4.0.3 + + '@types/supertest@6.0.3': + dependencies: + '@types/methods': 1.1.4 + '@types/superagent': 8.1.9 + '@vitest/coverage-v8@3.2.4(vitest@3.2.4)': dependencies: '@ampproject/remapping': 2.3.0 @@ -1705,13 +1910,13 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.0.1(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0))': + '@vitest/mocker@3.2.4(vite@7.0.4(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 7.0.1(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0) + vite: 7.0.4(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0) '@vitest/pretty-format@3.2.4': dependencies: @@ -1777,6 +1982,8 @@ snapshots: any-promise@1.3.0: {} + asap@2.0.6: {} + asn1.js@5.4.1: dependencies: bn.js: 4.12.2 @@ -1792,6 +1999,8 @@ snapshots: estree-walker: 3.0.3 js-tokens: 9.0.1 + asynckit@0.4.0: {} + atomic-sleep@1.0.0: {} avvio@9.1.0: @@ -1816,6 +2025,16 @@ snapshots: cac@6.7.14: {} + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + chai@5.2.0: dependencies: assertion-error: 2.0.1 @@ -1836,14 +2055,22 @@ snapshots: color-name@1.1.4: {} + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + commander@4.1.1: {} + component-emitter@1.3.1: {} + confbox@0.1.8: {} consola@3.4.2: {} cookie@1.0.2: {} + cookiejar@2.1.4: {} + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -1856,8 +2083,23 @@ snapshots: deep-eql@5.0.2: {} + delayed-stream@1.0.0: {} + dequal@2.0.3: {} + dezalgo@1.0.4: + dependencies: + asap: 2.0.6 + wrappy: 1.0.2 + + dotenv@17.2.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + eastasianwidth@0.2.0: {} ecdsa-sig-formatter@1.0.11: @@ -1868,8 +2110,23 @@ snapshots: emoji-regex@9.2.2: {} + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + es-module-lexer@1.7.0: {} + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + esbuild@0.25.5: optionalDependencies: '@esbuild/aix-ppc64': 0.25.5 @@ -1930,6 +2187,8 @@ snapshots: fast-redact@3.5.0: {} + fast-safe-stringify@2.1.1: {} + fast-uri@3.0.6: {} fastfall@1.5.1: @@ -1995,9 +2254,43 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + form-data@4.0.3: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + formidable@3.5.4: + dependencies: + '@paralleldrive/cuid2': 2.2.2 + dezalgo: 1.0.4 + once: 1.4.0 + fsevents@2.3.3: optional: true + function-bind@1.1.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + get-tsconfig@4.10.1: dependencies: resolve-pkg-maps: 1.0.0 @@ -2013,8 +2306,20 @@ snapshots: globrex@0.1.2: {} + gopd@1.2.0: {} + has-flag@4.0.0: {} + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + html-escaper@2.0.2: {} inherits@2.0.4: {} @@ -2096,6 +2401,18 @@ snapshots: dependencies: semver: 7.7.2 + math-intrinsics@1.1.0: {} + + methods@1.1.2: {} + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime@2.6.0: {} + minimalistic-assert@1.0.1: {} minimatch@9.0.5: @@ -2129,10 +2446,16 @@ snapshots: object-assign@4.1.1: {} + object-inspect@1.13.4: {} + obliterator@2.0.5: {} on-exit-leak-free@2.1.2: {} + once@1.4.0: + dependencies: + wrappy: 1.0.2 + package-json-from-dist@1.0.1: {} path-key@3.1.1: {} @@ -2206,6 +2529,10 @@ snapshots: punycode@2.3.1: {} + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + quick-format-unescaped@4.0.4: {} readdirp@4.1.2: {} @@ -2272,6 +2599,34 @@ snapshots: shebang-regex@3.0.0: {} + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} signal-exit@4.1.0: {} @@ -2340,6 +2695,27 @@ snapshots: pirates: 4.0.7 ts-interface-checker: 0.1.13 + superagent@10.2.2: + dependencies: + component-emitter: 1.3.1 + cookiejar: 2.1.4 + debug: 4.4.1 + fast-safe-stringify: 2.1.1 + form-data: 4.0.3 + formidable: 3.5.4 + methods: 1.1.2 + mime: 2.6.0 + qs: 6.14.0 + transitivePeerDependencies: + - supports-color + + supertest@7.1.3: + dependencies: + methods: 1.1.2 + superagent: 10.2.2 + transitivePeerDependencies: + - supports-color + supports-color@7.2.0: dependencies: has-flag: 4.0.0 @@ -2440,7 +2816,7 @@ snapshots: debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.0.1(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0) + vite: 7.0.4(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0) transitivePeerDependencies: - '@types/node' - jiti @@ -2455,18 +2831,18 @@ snapshots: - tsx - yaml - vite-tsconfig-paths@5.1.4(typescript@5.8.3)(vite@7.0.1(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0)): + vite-tsconfig-paths@5.1.4(typescript@5.8.3)(vite@7.0.4(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0)): dependencies: debug: 4.4.1 globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.8.3) optionalDependencies: - vite: 7.0.1(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0) + vite: 7.0.4(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0) transitivePeerDependencies: - supports-color - typescript - vite@7.0.1(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0): + vite@7.0.4(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0): dependencies: esbuild: 0.25.5 fdir: 6.4.6(picomatch@4.0.2) @@ -2485,7 +2861,7 @@ snapshots: dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.0.1(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0)) + '@vitest/mocker': 3.2.4(vite@7.0.4(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -2503,7 +2879,7 @@ snapshots: tinyglobby: 0.2.14 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.0.1(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0) + vite: 7.0.4(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0) vite-node: 3.2.4(@types/node@24.0.4)(jiti@2.4.2)(tsx@4.20.3)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: @@ -2552,6 +2928,8 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.1.0 + wrappy@1.0.2: {} + xtend@4.0.2: {} yaml@2.8.0: diff --git a/prisma/vitest-environment-prisma/prisma-test-environment.ts b/prisma/vitest-environment-prisma/prisma-test-environment.ts new file mode 100644 index 0000000..b7dfdba --- /dev/null +++ b/prisma/vitest-environment-prisma/prisma-test-environment.ts @@ -0,0 +1,47 @@ +import 'dotenv/config'; + +import { randomUUID } from 'node:crypto'; +import { execSync } from 'node:child_process'; +import { Environment } from 'vitest/environments'; +import { PrismaClient } from '../generated/prisma'; + +const prisma = new PrismaClient(); + +function generateDatabaseURL(schema: string) { + if (!process.env.DATABASE_URL) { + throw new Error('Please provide a DATABASE_URL environment variable.'); + } + + const url = new URL(process.env.DATABASE_URL); + + url.searchParams.set('schema', schema); + + return url.toString(); +} + +export default ({ + name: 'prisma', + transformMode: 'ssr', + async setup() { + const schema = randomUUID(); + const databaseURL = generateDatabaseURL(schema); + + process.env.DATABASE_URL = databaseURL; + + const randomNumberPort = + Math.floor(Math.random() * (9000 - 3000 + 1)) + 3000; + process.env.PORT = String(randomNumberPort); + + execSync('npx prisma migrate deploy'); + + return { + async teardown() { + await prisma.$executeRawUnsafe( + `DROP SCHEMA IF EXISTS "${schema}" CASCADE` + ); + + await prisma.$disconnect(); + }, + }; + }, +}); diff --git a/src/http/controllers/orgs/authOrgsController.spec.ts b/src/http/controllers/orgs/authOrgsController.spec.ts new file mode 100644 index 0000000..1d79880 --- /dev/null +++ b/src/http/controllers/orgs/authOrgsController.spec.ts @@ -0,0 +1,34 @@ +import { app } from "@/http/server"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import request from "supertest"; +import { makeOrg } from "@/use-cases/factories/makeOrg.factory"; + +describe('Autenticação (e2e)', () => { + beforeAll(async ()=>{ + await app.ready(); + }) + + afterAll(async ()=> { + await app.close(); + }) + + it('E possivel logar com uma organização', async ()=>{ + const inputCreateOrg = { + ...makeOrg(), + id: undefined + }; + + await request(app.server) + .post('/orgs') + .send(inputCreateOrg) + + const response = await request(app.server) + .post('/orgs/auth') + .send({ + email: inputCreateOrg.email, + password: inputCreateOrg.password + }) + + expect(response.statusCode).toEqual(200) + }) +}) \ No newline at end of file diff --git a/src/http/controllers/orgs/createOrgsController.spec.ts b/src/http/controllers/orgs/createOrgsController.spec.ts new file mode 100644 index 0000000..78d95bd --- /dev/null +++ b/src/http/controllers/orgs/createOrgsController.spec.ts @@ -0,0 +1,27 @@ +import { app } from "@/http/server"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import request from "supertest"; +import { makeOrg } from "@/use-cases/factories/makeOrg.factory"; + +describe('Cadastro de Organização (e2e)', () => { + beforeAll(async ()=>{ + await app.ready(); + }) + + afterAll(async()=> { + await app.close(); + }) + + it('E possivel cadastrar uma organização', async ()=>{ + const input = { + ...makeOrg(), + id: undefined + }; + + const response = await request(app.server) + .post('/orgs') + .send(input) + + expect(response.statusCode).toEqual(201) + }) +}) \ No newline at end of file diff --git a/src/http/controllers/orgs/getOrgsProfileController.spec.ts b/src/http/controllers/orgs/getOrgsProfileController.spec.ts new file mode 100644 index 0000000..8bf6cf8 --- /dev/null +++ b/src/http/controllers/orgs/getOrgsProfileController.spec.ts @@ -0,0 +1,42 @@ +import { app } from '@/http/server'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import request from 'supertest'; +import { makeOrg } from '@/use-cases/factories/makeOrg.factory'; + +describe('Autenticação (e2e)', () => { + beforeAll(async () => { + await app.ready(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('E possivel recuperar os dados de perfil de uma organização', async () => { + const inputCreateOrg = { + ...makeOrg(), + id: undefined, + }; + + await request(app.server).post('/orgs').send(inputCreateOrg); + + const authResponse = await request(app.server).post('/orgs/auth').send({ + email: inputCreateOrg.email, + password: inputCreateOrg.password, + }); + + const { token } = authResponse.body; + + const response = await request(app.server) + .get('/orgs/profile') + .set('Authorization', `Bearer ${token}`); + + expect(response.statusCode).toEqual(200); + expect(response.body).toEqual( + expect.objectContaining({ + email: inputCreateOrg.email, + name: inputCreateOrg.name, + }) + ); + }); +}); diff --git a/src/http/controllers/orgs/orgsRoutes.ts b/src/http/controllers/orgs/orgsRoutes.ts index af5ec09..1c0626c 100644 --- a/src/http/controllers/orgs/orgsRoutes.ts +++ b/src/http/controllers/orgs/orgsRoutes.ts @@ -1,15 +1,19 @@ -import { FastifyInstance } from "fastify"; -import { createOrgsController } from "./createOrgsController"; -import { authOrgsController } from "./authOrgsController"; -import { getOrgsProfileController } from "./getOrgsProfileController"; -import { authMiddleware } from "@/http/middlewares/authMiddleware"; -import { RefreshOrgsController } from "./refreshOrgsController"; +import { FastifyInstance } from 'fastify'; +import { createOrgsController } from './createOrgsController'; +import { authOrgsController } from './authOrgsController'; +import { getOrgsProfileController } from './getOrgsProfileController'; +import { authMiddleware } from '@/http/middlewares/authMiddleware'; +import { RefreshOrgsController } from './refreshOrgsController'; export const orgsRoutes = async (app: FastifyInstance) => { - app.post('/orgs', createOrgsController); + app.post('/orgs', createOrgsController); - app.post('/orgs/auth', authOrgsController); - app.patch('/orgs/refresh', RefreshOrgsController); + app.post('/orgs/auth', authOrgsController); + app.patch('/orgs/refresh', RefreshOrgsController); - app.get('/orgs/profile', { onRequest: [authMiddleware] }, getOrgsProfileController); -} \ No newline at end of file + app.get( + '/orgs/profile', + { onRequest: [authMiddleware] }, + getOrgsProfileController + ); +}; diff --git a/src/http/controllers/pets/createPetsController.spec.ts b/src/http/controllers/pets/createPetsController.spec.ts new file mode 100644 index 0000000..8ffd758 --- /dev/null +++ b/src/http/controllers/pets/createPetsController.spec.ts @@ -0,0 +1,50 @@ +import { app } from '@/http/server'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import request from 'supertest'; +import { makePet } from '@/use-cases/factories/makePet.factory'; +import { makeOrg } from '@/use-cases/factories/makeOrg.factory'; + +describe('Cadastro de Pet (e2e)', () => { + beforeAll(async () => { + await app.ready(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('E possivel cadastrar um pet', async () => { + const inputCreateOrg = { + ...makeOrg(), + id: undefined, + }; + + await request(app.server).post('/orgs').send(inputCreateOrg); + + const authResponse = await request(app.server).post('/orgs/auth').send({ + email: inputCreateOrg.email, + password: inputCreateOrg.password, + }); + + const { token } = authResponse.body; + + const inputCreatePet = { + ...makePet(), + id: undefined, + }; + + const response = await request(app.server) + .post('/pets') + .set('Authorization', `Bearer ${token}`) + .send(inputCreatePet); + + expect(response.statusCode).toEqual(201); + expect(response.body.pet.id).toEqual(expect.any(String)); + expect(response.body.pet).toEqual( + expect.objectContaining({ + name: inputCreatePet.name, + age: inputCreatePet.age, + }) + ); + }); +}); diff --git a/src/http/controllers/pets/createPetsController.ts b/src/http/controllers/pets/createPetsController.ts index 1b9cd72..7e61ba6 100644 --- a/src/http/controllers/pets/createPetsController.ts +++ b/src/http/controllers/pets/createPetsController.ts @@ -1,42 +1,39 @@ -import { OrgNotFoundError } from "@/use-cases/errors/orgNotFoundError"; -import { makeCreatePetsUseCase } from "@/use-cases/factories/makeCreatePetsUseCase"; -import { FastifyReply, FastifyRequest } from "fastify"; -import { z } from "zod/v4"; +import { OrgNotFoundError } from '@/use-cases/errors/orgNotFoundError'; +import { makeCreatePetsUseCase } from '@/use-cases/factories/makeCreatePetsUseCase'; +import type { FastifyReply, FastifyRequest } from 'fastify'; +import { z } from 'zod/v4'; export const createPetsController = async ( - request: FastifyRequest, - reply: FastifyReply, + request: FastifyRequest, + reply: FastifyReply ) => { - const orgId = request.user.sub; + const orgId = request.user.sub; - const createOrgsBodySchema = z.object({ - name: z.string(), - about: z.string(), - age: z.string(), - size: z.string(), - energy_level: z.string(), - environment: z.string(), - }); + const createPetsBodySchema = z.object({ + name: z.string(), + about: z.string(), + age: z.string(), + size: z.string(), + energy_level: z.string(), + environment: z.string(), + }); - const bodyData = createOrgsBodySchema.parse(request.body); + const bodyData = createPetsBodySchema.parse(request.body); - try - { - const createOrgsUseCase = makeCreatePetsUseCase(); - await createOrgsUseCase.execute({ - ...bodyData, - orgId - }); + try { + const createPetsUseCase = makeCreatePetsUseCase(); - } catch (error) - { - if (error instanceof OrgNotFoundError) - { - return reply.status(404).send(error.message); - } + const { pet } = await createPetsUseCase.execute({ + ...bodyData, + orgId, + }); - throw error; + reply.status(201).send({ pet }); + } catch (error) { + if (error instanceof OrgNotFoundError) { + return reply.status(404).send(error.message); } - reply.status(201).send(); -} + throw error; + } +}; diff --git a/src/http/controllers/pets/getPetsController.spec.ts b/src/http/controllers/pets/getPetsController.spec.ts new file mode 100644 index 0000000..44e427f --- /dev/null +++ b/src/http/controllers/pets/getPetsController.spec.ts @@ -0,0 +1,58 @@ +import { app } from '@/http/server'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import request from 'supertest'; +import { makePet } from '@/use-cases/factories/makePet.factory'; +import { makeOrg } from '@/use-cases/factories/makeOrg.factory'; + +describe('Recuperar dados do Pet (e2e)', () => { + beforeAll(async () => { + await app.ready(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('E possivel recuperar os dados de um pet', async () => { + const inputCreateOrg = { + ...makeOrg({ city: 'city_test' }), + id: undefined, + }; + + await request(app.server).post('/orgs').send(inputCreateOrg); + + const authResponse = await request(app.server).post('/orgs/auth').send({ + email: inputCreateOrg.email, + password: inputCreateOrg.password, + }); + + const { token } = authResponse.body; + + await Promise.all( + Array.from({ length: 5 }).map(() => + request(app.server) + .post('/pets') + .set('Authorization', `Bearer ${token}`) + .send({ + ...makePet(), + id: undefined, + }) + ) + ); + + const listPets = await request(app.server) + .get('/pets') + .query({ city: 'city_test', environment: 'indoor' }); + + const response = await request(app.server).get( + `/pets/${listPets.body.pets[0].id}` + ); + + expect(response.statusCode).toEqual(200); + expect(response.body).toEqual( + expect.objectContaining({ + name: listPets.body.pets[0].name, + }) + ); + }); +}); diff --git a/src/http/controllers/pets/getPetsController.ts b/src/http/controllers/pets/getPetsController.ts index 4127b1c..261a60e 100644 --- a/src/http/controllers/pets/getPetsController.ts +++ b/src/http/controllers/pets/getPetsController.ts @@ -1,31 +1,28 @@ import { ResourceNotFoundError } from '@/use-cases/errors/resourceNotFound'; import { makeGetPetsUseCase } from '@/use-cases/factories/makeGetPetsUseCase'; -import { FastifyReply, FastifyRequest } from "fastify"; -import { z } from "zod/v4"; +import type { FastifyReply, FastifyRequest } from 'fastify'; +import { z } from 'zod/v4'; export const getPetsController = async ( - request: FastifyRequest, - reply: FastifyReply, + request: FastifyRequest, + reply: FastifyReply ) => { - const getPetsQuerySchema = z.object({ - id: z.string() - }); + const getPetsQuerySchema = z.object({ + id: z.string(), + }); - const paramsData = getPetsQuerySchema.parse(request.params); + const paramsData = getPetsQuerySchema.parse(request.params); - try - { - const getPetsUseCase = makeGetPetsUseCase(); - const { pet } = await getPetsUseCase.execute(paramsData); + try { + const getPetsUseCase = makeGetPetsUseCase(); + const { pet } = await getPetsUseCase.execute(paramsData); - reply.status(200).send(pet); - } catch (error) - { - if (error instanceof ResourceNotFoundError) - { - return reply.status(404).send(error.message); - } - - throw error; + reply.status(200).send(pet); + } catch (error) { + if (error instanceof ResourceNotFoundError) { + return reply.status(404).send(error.message); } -} + + throw error; + } +}; diff --git a/src/http/controllers/pets/listPetsController.spec.ts b/src/http/controllers/pets/listPetsController.spec.ts new file mode 100644 index 0000000..c40b673 --- /dev/null +++ b/src/http/controllers/pets/listPetsController.spec.ts @@ -0,0 +1,56 @@ +import { app } from '@/http/server'; +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import request from 'supertest'; +import { makePet } from '@/use-cases/factories/makePet.factory'; +import { makeOrg } from '@/use-cases/factories/makeOrg.factory'; +import type { Pets } from 'prisma/generated/prisma'; + +describe('Lista de Pets de uma cidade (e2e)', () => { + beforeAll(async () => { + await app.ready(); + }); + + afterAll(async () => { + await app.close(); + }); + + it('E possivel listar os pets de uma cidade', async () => { + const inputCreateOrg = { + ...makeOrg({ city: 'city_test' }), + id: undefined, + }; + + await request(app.server).post('/orgs').send(inputCreateOrg); + + const authResponse = await request(app.server).post('/orgs/auth').send({ + email: inputCreateOrg.email, + password: inputCreateOrg.password, + }); + + const { token } = authResponse.body; + + await Promise.all( + Array.from({ length: 5 }).map((_, i) => + request(app.server) + .post('/pets') + .set('Authorization', `Bearer ${token}`) + .send({ + ...makePet({ + environment: i % 2 ? 'indoor' : 'outdoor', + }), + id: undefined, + }) + ) + ); + + const response = await request(app.server) + .get('/pets') + .query({ city: 'city_test', environment: 'indoor' }); + + expect(response.statusCode).toEqual(200); + expect(response.body.pets).toHaveLength(2); + expect( + response.body.pets.every((pet: Pets) => pet.environment === 'indoor') + ).toBe(true); + }); +}); diff --git a/src/http/controllers/pets/listPetsController.ts b/src/http/controllers/pets/listPetsController.ts index 7b0bf06..0110958 100644 --- a/src/http/controllers/pets/listPetsController.ts +++ b/src/http/controllers/pets/listPetsController.ts @@ -1,29 +1,27 @@ -import { makeListPetsUseCase } from "@/use-cases/factories/makeListPetsUseCase"; -import { FastifyReply, FastifyRequest } from "fastify"; -import { z } from "zod/v4"; +import { makeListPetsUseCase } from '@/use-cases/factories/makeListPetsUseCase'; +import { FastifyReply, FastifyRequest } from 'fastify'; +import { z } from 'zod/v4'; export const listPetsController = async ( - request: FastifyRequest, - reply: FastifyReply, + request: FastifyRequest, + reply: FastifyReply ) => { - const getPetsQuerySchema = z.object({ - city: z.string(), - age: z.string().optional(), - size: z.string().optional(), - energy_level: z.string().optional(), - environment: z.string().optional() - }); + const getPetsQuerySchema = z.object({ + city: z.string(), + age: z.string().optional(), + size: z.string().optional(), + energy_level: z.string().optional(), + environment: z.string().optional(), + }); - const queryData = getPetsQuerySchema.parse(request.query); + const queryData = getPetsQuerySchema.parse(request.query); - try - { - const listPetsUseCase = makeListPetsUseCase(); - const { pets } = await listPetsUseCase.execute(queryData); + try { + const listPetsUseCase = makeListPetsUseCase(); + const { pets } = await listPetsUseCase.execute(queryData); - reply.status(200).send(pets); - } catch (error) - { - throw error; - } -} + reply.status(200).send({ pets }); + } catch (error) { + throw error; + } +}; diff --git a/src/http/controllers/status/statusRouts.spec.ts b/src/http/controllers/status/statusRouts.spec.ts new file mode 100644 index 0000000..7ecc082 --- /dev/null +++ b/src/http/controllers/status/statusRouts.spec.ts @@ -0,0 +1,22 @@ +import { makeOrg } from "@/use-cases/factories/makeOrg.factory"; +import { app } from "@/http/server"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import request from "supertest"; + +describe('Endpoint (e2e) : Status da Aplicação', () => { + beforeAll(async ()=>{ + await app.ready(); + }) + + afterAll(async()=> { + await app.close(); + }) + + it('Aplicação esta com status OK', async ()=>{ + + const response = await request(app.server).get('/status'); + + + expect(response.statusCode).toEqual(200); + }) +}) \ No newline at end of file diff --git a/src/http/controllers/status/statusRouts.ts b/src/http/controllers/status/statusRouts.ts index 55acb80..96e3d71 100644 --- a/src/http/controllers/status/statusRouts.ts +++ b/src/http/controllers/status/statusRouts.ts @@ -5,6 +5,9 @@ export const statusRoutes = async (app: FastifyInstance) => { request: FastifyRequest, reply: FastifyReply, ) => { - reply.status(200).send("API :ok"); + + reply.status(200).send({ + api: 'OK' + }); }); } \ No newline at end of file diff --git a/src/repositories/in-memory/petsRepositoryInMemory.ts b/src/repositories/in-memory/petsRepositoryInMemory.ts index 794de75..9b52b70 100644 --- a/src/repositories/in-memory/petsRepositoryInMemory.ts +++ b/src/repositories/in-memory/petsRepositoryInMemory.ts @@ -1,52 +1,49 @@ - - -import { randomUUID } from 'node:crypto' -import { FindAllParams, IPetsRepository } from '@/repositories/IPetsRepository' -import { Pets, Prisma } from 'prisma/generated/prisma' -import { OrgsRepositoryInMemory } from './orgsRepositoryInMemory' +import { randomUUID } from 'node:crypto'; +import { FindAllParams, IPetsRepository } from '@/repositories/IPetsRepository'; +import { Pets, Prisma } from 'prisma/generated/prisma'; +import { OrgsRepositoryInMemory } from './orgsRepositoryInMemory'; export class PetsRepositoryInMemory implements IPetsRepository { - public items: Pets[] = [] - constructor(private orgsRepository: OrgsRepositoryInMemory) { } + public items: Pets[] = []; + constructor(private orgsRepository: OrgsRepositoryInMemory) {} - async create({ id, ...data }: Prisma.PetsCreateManyInput) { - const pet: Pets = { - ...data, - id: id ?? randomUUID(), - } + async create({ id, ...data }: Prisma.PetsCreateManyInput) { + const pet: Pets = { + ...data, + id: id ?? randomUUID(), + }; - this.items.push(pet) + this.items.push(pet); - return pet; - } + return pet; + } - async findById(id: string) { - const pet = this.items.find((item) => item.id === id) + async findById(id: string) { + const pet = this.items.find(item => item.id === id); - if (!pet) - { - return null - } - - return pet + if (!pet) { + return null; } - async findAll(params: FindAllParams): Promise { - const orgsByCity = this.orgsRepository.items.filter( - (org) => org.city === params.city, - ) - - const pets = this.items - .filter((item) => orgsByCity.some((org) => org.id === item.org_id)) - .filter((item) => (params.age ? item.age === params.age : true)) - .filter((item) => (params.size ? item.size === params.size : true)) - .filter((item) => - params.energy_level ? item.energy_level === params.energy_level : true, - ) - .filter((item) => - params.environment ? item.environment === params.environment : true, - ) - - return pets - } -} \ No newline at end of file + return pet; + } + + async findAll(params: FindAllParams): Promise { + const orgsByCity = this.orgsRepository.items.filter( + org => org.city === params.city + ); + + const pets = this.items + .filter(item => orgsByCity.some(org => org.id === item.org_id)) + .filter(item => (params.age ? item.age === params.age : true)) + .filter(item => (params.size ? item.size === params.size : true)) + .filter(item => + params.energy_level ? item.energy_level === params.energy_level : true + ) + .filter(item => + params.environment ? item.environment === params.environment : true + ); + + return pets; + } +} diff --git a/src/use-cases/factories/makeOrg.factory.ts b/src/use-cases/factories/makeOrg.factory.ts index 66508d4..b1beb23 100644 --- a/src/use-cases/factories/makeOrg.factory.ts +++ b/src/use-cases/factories/makeOrg.factory.ts @@ -1,25 +1,25 @@ -import { faker } from '@faker-js/faker' -import crypto from 'node:crypto' +import { faker } from '@faker-js/faker'; +import crypto from 'node:crypto'; type Overwrite = { - password?: string - city?: string -} + password?: string; + city?: string; +}; export function makeOrg(overwrite?: Overwrite) { - return { - id: crypto.randomUUID(), - author_name: faker.person.fullName(), - cep: faker.location.zipCode(), - city: faker.location.city(), - email: faker.internet.email(), - latitude: faker.location.latitude(), - longitude: faker.location.longitude(), - name: faker.company.name(), - neighborhood: faker.location.streetAddress(), - password: overwrite?.password ?? faker.internet.password(), - state: faker.location.state(), - street: faker.location.street(), - whatsapp: faker.phone.number(), - } -} \ No newline at end of file + return { + id: crypto.randomUUID(), + author_name: faker.person.fullName(), + cep: faker.location.zipCode(), + city: overwrite?.city ?? faker.location.city(), + email: faker.internet.email(), + latitude: faker.location.latitude(), + longitude: faker.location.longitude(), + name: faker.company.name(), + neighborhood: faker.location.streetAddress(), + password: overwrite?.password ?? faker.internet.password(), + state: faker.location.state(), + street: faker.location.street(), + whatsapp: faker.phone.number(), + }; +} diff --git a/src/use-cases/factories/makePet.factory.ts b/src/use-cases/factories/makePet.factory.ts index 1a5c0bf..3bed445 100644 --- a/src/use-cases/factories/makePet.factory.ts +++ b/src/use-cases/factories/makePet.factory.ts @@ -1,27 +1,29 @@ -import { faker } from '@faker-js/faker' -import crypto from 'node:crypto' +import { faker } from '@faker-js/faker'; +import crypto from 'node:crypto'; type Overwrite = { - org_id?: string - age?: string - size?: string - energy_level?: string - environment?: string -} + org_id?: string; + age?: string; + size?: string; + energy_level?: string; + environment?: string; +}; export function makePet(overwrite?: Overwrite) { - return { - id: crypto.randomUUID(), - org_id: overwrite?.org_id ?? crypto.randomUUID(), - name: faker.animal.dog(), - about: faker.lorem.paragraph(), - age: overwrite?.age ?? faker.number.int().toString(), - size: - overwrite?.size ?? - faker.helpers.arrayElement(['small', 'medium', 'large']), - energy_level: - overwrite?.energy_level ?? - faker.helpers.arrayElement(['low', 'medium', 'high']), - environment: faker.helpers.arrayElement(['indoor', 'outdoor']), - } -} \ No newline at end of file + return { + id: crypto.randomUUID(), + org_id: overwrite?.org_id ?? crypto.randomUUID(), + name: faker.animal.dog(), + about: faker.lorem.paragraph(), + age: overwrite?.age ?? faker.number.int().toString(), + size: + overwrite?.size ?? + faker.helpers.arrayElement(['small', 'medium', 'large']), + energy_level: + overwrite?.energy_level ?? + faker.helpers.arrayElement(['low', 'medium', 'high']), + environment: + overwrite?.environment ?? + faker.helpers.arrayElement(['indoor', 'outdoor']), + }; +} diff --git a/src/use-cases/pets/createPetsUseCase.spec.ts b/src/use-cases/pets/createPetsUseCase.spec.ts index 844c469..51f9994 100644 --- a/src/use-cases/pets/createPetsUseCase.spec.ts +++ b/src/use-cases/pets/createPetsUseCase.spec.ts @@ -5,7 +5,7 @@ import { OrgsRepositoryInMemory } from '@/repositories/in-memory/orgsRepositoryI import { OrgNotFoundError } from '../errors/orgNotFoundError'; import { makeOrg } from '../factories/makeOrg.factory'; -describe("Caso de Uso: Criação de Pets", () => { +describe('Caso de Uso: Criação de Pets', () => { let orgsRepository: OrgsRepositoryInMemory; let petsRepository: PetsRepositoryInMemory; let sut: CreatePetsUseCase; @@ -14,9 +14,9 @@ describe("Caso de Uso: Criação de Pets", () => { orgsRepository = new OrgsRepositoryInMemory(); petsRepository = new PetsRepositoryInMemory(orgsRepository); sut = new CreatePetsUseCase(petsRepository, orgsRepository); - }) + }); - it("Deve ser possivel cadastrar um Pet", async () => { + it('Deve ser possivel cadastrar um Pet', async () => { const org = await orgsRepository.create(makeOrg()); const { pet } = await sut.execute({ @@ -26,16 +26,14 @@ describe("Caso de Uso: Criação de Pets", () => { size: 'Grande', energy_level: '2', environment: 'asdf', - orgId: org.id + orgId: org.id, }); - expect(pet).toHaveProperty('id') - expect(pet.id).toEqual(expect.any(String)) - + expect(pet).toHaveProperty('id'); + expect(pet.id).toEqual(expect.any(String)); }); - it("Não deve ser possivel cadastrar um Pet para uma organização inexistente", async () => { - + it('Não deve ser possivel cadastrar um Pet para uma organização inexistente', async () => { await expect( sut.execute({ name: 'Alfredo', @@ -48,5 +46,4 @@ describe("Caso de Uso: Criação de Pets", () => { }) ).rejects.instanceOf(OrgNotFoundError); }); - }); diff --git a/src/use-cases/pets/createPetsUseCase.ts b/src/use-cases/pets/createPetsUseCase.ts index a3c21f8..af7db9d 100644 --- a/src/use-cases/pets/createPetsUseCase.ts +++ b/src/use-cases/pets/createPetsUseCase.ts @@ -1,45 +1,42 @@ -import { IOrgsRepository } from "@/repositories/IOrgsRepository"; -import { IPetsRepository } from "@/repositories/IPetsRepository"; -import { Pets } from "prisma/generated/prisma"; -import { OrgNotFoundError } from "../errors/orgNotFoundError"; +import { IOrgsRepository } from '@/repositories/IOrgsRepository'; +import { IPetsRepository } from '@/repositories/IPetsRepository'; +import { Pets } from 'prisma/generated/prisma'; +import { OrgNotFoundError } from '../errors/orgNotFoundError'; type CreatePetsCaseRequest = { - orgId: string; - name: string; - about: string; - age: string; - size: string; - energy_level: string; - environment: string; -} + orgId: string; + name: string; + about: string; + age: string; + size: string; + energy_level: string; + environment: string; +}; interface CreatePetsCaseResponse { - pet: Pets + pet: Pets; } export class CreatePetsUseCase { + constructor( + private petsRepository: IPetsRepository, + private orgsRepository: IOrgsRepository + ) {} + + async execute({ + orgId, + ...data + }: CreatePetsCaseRequest): Promise { + const org = await this.orgsRepository.findById(orgId); + if (!org) { + throw new OrgNotFoundError(); + } - constructor( - private petsRepository: IPetsRepository, - private orgsRepository: IOrgsRepository - - ) { } - - async execute({ - orgId, - ...data - }: CreatePetsCaseRequest): Promise { - const org = await this.orgsRepository.findById(orgId) - if (!org) - { - throw new OrgNotFoundError() - } - - const pet = await this.petsRepository.create({ - ...data, - org_id: orgId, - }) + const pet = await this.petsRepository.create({ + ...data, + org_id: orgId, + }); - return { pet }; - } -} \ No newline at end of file + return { pet }; + } +} diff --git a/src/use-cases/pets/listPetUseCase.spec.ts b/src/use-cases/pets/listPetUseCase.spec.ts index 1a171d9..232b45f 100644 --- a/src/use-cases/pets/listPetUseCase.spec.ts +++ b/src/use-cases/pets/listPetUseCase.spec.ts @@ -5,98 +5,142 @@ import { ListPetUseCase } from './listPetUseCase'; import { makePet } from '../factories/makePet.factory'; import { makeOrg } from '../factories/makeOrg.factory'; -describe("Caso de Uso: Get Pet", () => { - let orgsRepository: OrgsRepositoryInMemory; - let petsRepository: PetsRepositoryInMemory; - let sut: ListPetUseCase; +describe('Caso de Uso: Get Pet', () => { + let orgsRepository: OrgsRepositoryInMemory; + let petsRepository: PetsRepositoryInMemory; + let sut: ListPetUseCase; - beforeEach(async () => { - orgsRepository = new OrgsRepositoryInMemory(); - petsRepository = new PetsRepositoryInMemory(orgsRepository); + beforeEach(async () => { + orgsRepository = new OrgsRepositoryInMemory(); + petsRepository = new PetsRepositoryInMemory(orgsRepository); - sut = new ListPetUseCase(petsRepository); - }) + sut = new ListPetUseCase(petsRepository); + }); - it("Deve ser possivel listar os pets de uma cidade", async () => { - const org = await orgsRepository.create(makeOrg({ city: 'city1' })); - await petsRepository.create(makePet({ org_id: org.id })) - await petsRepository.create(makePet({ org_id: org.id })) + it('Deve ser possivel listar os pets de uma cidade', async () => { + const org = await orgsRepository.create(makeOrg({ city: 'city1' })); + await petsRepository.create(makePet({ org_id: org.id })); + await petsRepository.create(makePet({ org_id: org.id })); - const org2 = await orgsRepository.create(makeOrg({ city: 'city2' })); - await petsRepository.create(makePet({ org_id: org2.id })) + const org2 = await orgsRepository.create(makeOrg({ city: 'city2' })); + await petsRepository.create(makePet({ org_id: org2.id })); - const { pets } = await sut.execute({ city: org.city }) + const { pets } = await sut.execute({ city: org.city }); - expect(pets).toHaveLength(2) + expect(pets).toHaveLength(2); - const { pets: pets2 } = await sut.execute({ city: org2.city }) - expect(pets2).toHaveLength(1) - }); + const { pets: pets2 } = await sut.execute({ city: org2.city }); + expect(pets2).toHaveLength(1); + }); - it("Deve ser possivel listar os pets de uma cidade filtrando por 'age'", async () => { - const org = await orgsRepository.create(makeOrg({ city: 'city1' })); - await petsRepository.create(makePet({ org_id: org.id, age: '1' })) - await petsRepository.create(makePet({ org_id: org.id, age: '1' })) - await petsRepository.create(makePet({ org_id: org.id, age: '2' })) + it("Deve ser possivel listar os pets de uma cidade filtrando por 'age'", async () => { + const org = await orgsRepository.create(makeOrg({ city: 'city1' })); + await petsRepository.create(makePet({ org_id: org.id, age: '1' })); + await petsRepository.create(makePet({ org_id: org.id, age: '1' })); + await petsRepository.create(makePet({ org_id: org.id, age: '2' })); - const org2 = await orgsRepository.create(makeOrg({ city: 'city2' })); - await petsRepository.create(makePet({ org_id: org2.id, age: '1' })) - await petsRepository.create(makePet({ org_id: org2.id, age: '2' })) + const org2 = await orgsRepository.create(makeOrg({ city: 'city2' })); + await petsRepository.create(makePet({ org_id: org2.id, age: '1' })); + await petsRepository.create(makePet({ org_id: org2.id, age: '2' })); - const { pets } = await sut.execute({ city: org.city, age: '1' }) - expect(pets).toHaveLength(2) + const { pets } = await sut.execute({ city: org.city, age: '1' }); + expect(pets).toHaveLength(2); - const { pets: pets2 } = await sut.execute({ city: org2.city, age: '3' }) - expect(pets2).toHaveLength(0) - }); + const { pets: pets2 } = await sut.execute({ city: org2.city, age: '3' }); + expect(pets2).toHaveLength(0); + }); - it("Deve ser possivel listar os pets de uma cidade filtrando por 'size'", async () => { - const org = await orgsRepository.create(makeOrg({ city: 'city1' })); - await petsRepository.create(makePet({ org_id: org.id, size: 'small' })) - await petsRepository.create(makePet({ org_id: org.id, size: 'small' })) - await petsRepository.create(makePet({ org_id: org.id, size: 'medium' })) + it("Deve ser possivel listar os pets de uma cidade filtrando por 'size'", async () => { + const org = await orgsRepository.create(makeOrg({ city: 'city1' })); + await petsRepository.create(makePet({ org_id: org.id, size: 'small' })); + await petsRepository.create(makePet({ org_id: org.id, size: 'small' })); + await petsRepository.create(makePet({ org_id: org.id, size: 'medium' })); - const org2 = await orgsRepository.create(makeOrg({ city: 'city2' })); - await petsRepository.create(makePet({ org_id: org2.id, size: 'small' })) - await petsRepository.create(makePet({ org_id: org2.id, size: 'medium' })) + const org2 = await orgsRepository.create(makeOrg({ city: 'city2' })); + await petsRepository.create(makePet({ org_id: org2.id, size: 'small' })); + await petsRepository.create(makePet({ org_id: org2.id, size: 'medium' })); - const { pets } = await sut.execute({ city: org.city, size: 'small' }) - expect(pets).toHaveLength(2) + const { pets } = await sut.execute({ city: org.city, size: 'small' }); + expect(pets).toHaveLength(2); - const { pets: pets2 } = await sut.execute({ city: org2.city, size: 'large' }) - expect(pets2).toHaveLength(0) + const { pets: pets2 } = await sut.execute({ + city: org2.city, + size: 'large', }); - - it("Deve ser possivel listar os pets de uma cidade filtrando por 'energy_level'", async () => { - const org = await orgsRepository.create(makeOrg({ city: 'city1' })); - - await petsRepository.create(makePet({ org_id: org.id, energy_level: 'low' })) - await petsRepository.create(makePet({ org_id: org.id, energy_level: 'low' })) - await petsRepository.create(makePet({ org_id: org.id, energy_level: 'medium' })) - await petsRepository.create(makePet({ org_id: org.id, energy_level: 'high' })) - - const org2 = await orgsRepository.create(makeOrg({ city: 'city2' })); - await petsRepository.create(makePet({ org_id: org2.id, energy_level: 'low' })) - await petsRepository.create(makePet({ org_id: org2.id, energy_level: 'medium' })) - - const { pets } = await sut.execute({ city: org.city, energy_level: 'low' }) - expect(pets).toHaveLength(2) - - const { pets: pets2 } = await sut.execute({ city: org2.city, energy_level: 'high' }) - expect(pets2).toHaveLength(0) + expect(pets2).toHaveLength(0); + }); + + it("Deve ser possivel listar os pets de uma cidade filtrando por 'energy_level'", async () => { + const org = await orgsRepository.create(makeOrg({ city: 'city1' })); + + await petsRepository.create( + makePet({ org_id: org.id, energy_level: 'low' }) + ); + await petsRepository.create( + makePet({ org_id: org.id, energy_level: 'low' }) + ); + await petsRepository.create( + makePet({ org_id: org.id, energy_level: 'medium' }) + ); + await petsRepository.create( + makePet({ org_id: org.id, energy_level: 'high' }) + ); + + const org2 = await orgsRepository.create(makeOrg({ city: 'city2' })); + await petsRepository.create( + makePet({ org_id: org2.id, energy_level: 'low' }) + ); + await petsRepository.create( + makePet({ org_id: org2.id, energy_level: 'medium' }) + ); + + const { pets } = await sut.execute({ city: org.city, energy_level: 'low' }); + expect(pets).toHaveLength(2); + + const { pets: pets2 } = await sut.execute({ + city: org2.city, + energy_level: 'high', }); - - it("Deve ser possivel listar os pets de uma cidade filtrando por 'environment'", async () => { - const org = await orgsRepository.create(makeOrg({ city: 'city1' })); - - await petsRepository.create(makePet({ org_id: org.id, environment: 'indoor' })) - await petsRepository.create(makePet({ org_id: org.id, environment: 'indoor' })) - - await petsRepository.create(makePet({ org_id: org.id, environment: 'outdoor' })) - - const { pets } = await sut.execute({ city: org.city, environment: 'indoor' }) - expect(pets).toHaveLength(2) - + expect(pets2).toHaveLength(0); + }); + + it("Deve ser possivel listar os pets de uma cidade filtrando por 'environment'", async () => { + const org = await orgsRepository.create(makeOrg({ city: 'city1' })); + + await petsRepository.create( + makePet({ org_id: org.id, environment: 'indoor' }) + ); + await petsRepository.create( + makePet({ org_id: org.id, environment: 'indoor' }) + ); + + await petsRepository.create( + makePet({ org_id: org.id, environment: 'outdoor' }) + ); + + const org2 = await orgsRepository.create(makeOrg({ city: 'city2' })); + + await petsRepository.create( + makePet({ org_id: org2.id, environment: 'indoor' }) + ); + await petsRepository.create( + makePet({ org_id: org2.id, environment: 'indoor' }) + ); + + await petsRepository.create( + makePet({ org_id: org2.id, environment: 'outdoor' }) + ); + + const city1 = await sut.execute({ + city: 'city1', + environment: 'indoor', }); + expect(city1.pets).toHaveLength(2); + const city2 = await sut.execute({ + city: 'city2', + environment: 'outdoor', + }); + expect(city2.pets).toHaveLength(1); + }); }); diff --git a/src/use-cases/pets/listPetUseCase.ts b/src/use-cases/pets/listPetUseCase.ts index cfb1c13..731c3b2 100644 --- a/src/use-cases/pets/listPetUseCase.ts +++ b/src/use-cases/pets/listPetUseCase.ts @@ -1,30 +1,26 @@ -import { IPetsRepository } from "@/repositories/IPetsRepository"; -import { Pets } from "prisma/generated/prisma"; -import { ResourceNotFoundError } from "../errors/resourceNotFound"; +import { IPetsRepository } from '@/repositories/IPetsRepository'; +import { Pets } from 'prisma/generated/prisma'; interface ListPetUseCaseRequest { - city: string; - age?: string; - size?: string; - energy_level?: string; - environment?: string; + city: string; + age?: string; + size?: string; + energy_level?: string; + environment?: string; } interface ListPetUseCaseResponse { - pets: Pets[] + pets: Pets[]; } export class ListPetUseCase { + constructor(private petsRepository: IPetsRepository) {} - constructor( - private petsRepository: IPetsRepository - ) { } + async execute(data: ListPetUseCaseRequest): Promise { + const pets = await this.petsRepository.findAll({ ...data }); - async execute(data: ListPetUseCaseRequest): Promise { - const pets = await this.petsRepository.findAll({ ...data }) - - return { - pets - } - } -} \ No newline at end of file + return { + pets, + }; + } +} diff --git a/vite.config.mjs b/vite.config.mjs new file mode 100644 index 0000000..13a4bf3 --- /dev/null +++ b/vite.config.mjs @@ -0,0 +1,27 @@ +import { defineConfig } from 'vitest/config'; +import tsConfigPaths from 'vite-tsconfig-paths'; + +export default defineConfig({ + plugins: [tsConfigPaths()], + test: { + dir: 'src', + projects: [ + { + extends: true, + test: { + name: 'unit', + dir: 'src/use-cases', + }, + }, + { + extends: true, + test: { + name: 'e2e', + dir: 'src/http/controllers', + environment: + './prisma/vitest-environment-prisma/prisma-test-environment.ts', + }, + }, + ], + }, +}); diff --git a/vite.config.ts b/vite.config.ts deleted file mode 100644 index 9a92dda..0000000 --- a/vite.config.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { defineConfig } from 'vitest/config'; -import tsConfigPaths from 'vite-tsconfig-paths'; - -export default defineConfig({ - plugins: [tsConfigPaths()], -}); \ No newline at end of file