diff --git a/azure-pipelines-staging.yml b/azure-pipelines-staging.yml index 86f970d..65c19c4 100644 --- a/azure-pipelines-staging.yml +++ b/azure-pipelines-staging.yml @@ -27,9 +27,30 @@ jobs: exit 1 fi displayName: 'タグが付けられたCommitがmainブランチに存在するか確認' -- job: backend_build +- job: backend_test dependsOn: initialize condition: succeeded('initialize') + displayName: UnitTest + pool: + vmImage: ubuntu-latest + steps: + - checkout: self + clean: true + fetchDepth: 1 + - task: Bash@3 + displayName: Bash Script (Test) + inputs: + targetType: inline + workingDirectory: dictation_server/.devcontainer + script: | + docker-compose -f pipeline-docker-compose.yml build + docker-compose -f pipeline-docker-compose.yml up -d + docker-compose exec -T dictation_server sudo npm ci + docker-compose exec -T dictation_server sudo npm run migrate:up:test + docker-compose exec -T dictation_server sudo npm run test +- job: backend_build + dependsOn: backend_test + condition: succeeded('backend_test') displayName: Build And Push Backend Image pool: name: odms-deploy-pipeline @@ -43,51 +64,6 @@ jobs: command: ci workingDir: dictation_server verbose: false - - task: AzureKeyVault@2 - displayName: 'Azure Key Vault: kv-odms-secret-stg' - inputs: - ConnectedServiceName: 'omds-service-connection-stg' - KeyVaultName: kv-odms-secret-stg - SecretsFilter: '*' - - task: Bash@3 - displayName: Bash Script (Test) - inputs: - targetType: inline - script: | - cd dictation_server - npm run test - env: - JWT_PUBLIC_KEY: $(token-public-key) - JWT_PRIVATE_KEY: $(token-private-key) - SENDGRID_API_KEY: $(sendgrid-api-key) - NOTIFICATION_HUB_NAME: $(notification-hub-name) - NOTIFICATION_HUB_CONNECT_STRING: $(notification-hub-connect-string) - STORAGE_ACCOUNT_NAME_US: $(storage-account-name-us) - STORAGE_ACCOUNT_NAME_AU: $(storage-account-name-au) - STORAGE_ACCOUNT_NAME_EU: $(storage-account-name-eu) - STORAGE_ACCOUNT_KEY_US: $(storage-account-key-us) - STORAGE_ACCOUNT_KEY_AU: $(storage-account-key-au) - STORAGE_ACCOUNT_KEY_EU: $(storage-account-key-eu) - STORAGE_ACCOUNT_ENDPOINT_US: $(storage-account-endpoint-us) - STORAGE_ACCOUNT_ENDPOINT_AU: $(storage-account-endpoint-au) - STORAGE_ACCOUNT_ENDPOINT_EU: $(storage-account-endpoint-eu) - ADB2C_TENANT_ID: $(adb2c-tenant-id) - ADB2C_CLIENT_ID: $(adb2c-client-id) - ADB2C_CLIENT_SECRET: $(adb2c-client-secret) - MAIL_FROM: xxxxxx - APP_DOMAIN: xxxxxxxxx - EMAIL_CONFIRM_LIFETIME: 0 - TENANT_NAME: xxxxxxxxxxxx - SIGNIN_FLOW_NAME: xxxxxxxxxxxx - STORAGE_TOKEN_EXPIRE_TIME: 0 - REFRESH_TOKEN_LIFETIME_WEB: 86400000 - REFRESH_TOKEN_LIFETIME_DEFAULT: 2592000000 - ACCESS_TOKEN_LIFETIME_WEB: 7200000 - REDIS_HOST: xxxxxxxxxxxx - REDIS_PORT: 0 - REDIS_PASSWORD: xxxxxxxxxxxx - ADB2C_CACHE_TTL: 0 - STAGE: local - task: Docker@0 displayName: build inputs: diff --git a/dictation_client/src/pages/AccountPage/index.tsx b/dictation_client/src/pages/AccountPage/index.tsx index 79963ae..6fd0877 100644 --- a/dictation_client/src/pages/AccountPage/index.tsx +++ b/dictation_client/src/pages/AccountPage/index.tsx @@ -102,6 +102,7 @@ const AccountPage: React.FC = (): JSX.Element => {
+ {/* File Delete Setting は現状不要のため非表示 + */}
diff --git a/dictation_client/src/pages/AuthPage/index.tsx b/dictation_client/src/pages/AuthPage/index.tsx index 5a66e4c..e8d81f9 100644 --- a/dictation_client/src/pages/AuthPage/index.tsx +++ b/dictation_client/src/pages/AuthPage/index.tsx @@ -67,9 +67,6 @@ const AuthPage: React.FC = (): JSX.Element => { return; } const loginResult = await instance.handleRedirectPromise(); - - // eslint-disable-next-line - console.log({ loginResult }); // TODO:loading画面から遷移できない事象の調査用ログ。事象解消後削除(eslint-disable含めて)する。 if (loginResult && loginResult.account) { const { homeAccountId, idTokenClaims } = loginResult.account; if (idTokenClaims && idTokenClaims.aud) { diff --git a/dictation_client/src/pages/LicensePage/licenseSummary.tsx b/dictation_client/src/pages/LicensePage/licenseSummary.tsx index 058350d..a343fa5 100644 --- a/dictation_client/src/pages/LicensePage/licenseSummary.tsx +++ b/dictation_client/src/pages/LicensePage/licenseSummary.tsx @@ -289,13 +289,17 @@ export const LicenseSummary: React.FC = ( ) )} -
{licenseSummaryInfo.storageSize}GB
+ {/* Storage Usedの値表示をハイフンに置き換え */} + {/*
{licenseSummaryInfo.storageSize}GB
*/} +
-
{t( getTranslationID("LicenseSummaryPage.label.usedSize") )}
-
{licenseSummaryInfo.usedSize}GB
+ {/* Storage Usedの値表示をハイフンに置き換え */} + {/*
{licenseSummaryInfo.usedSize}GB
*/} +
-
{t( getTranslationID( diff --git a/dictation_client/src/pages/PartnerPage/index.tsx b/dictation_client/src/pages/PartnerPage/index.tsx index 7f73f66..835cfa3 100644 --- a/dictation_client/src/pages/PartnerPage/index.tsx +++ b/dictation_client/src/pages/PartnerPage/index.tsx @@ -185,6 +185,7 @@ const PartnerPage: React.FC = (): JSX.Element => {
    + {/* パートナーアカウント削除はCCB後回し分なので非表示 {isVisibleButton && (
  • @@ -196,6 +197,7 @@ const PartnerPage: React.FC = (): JSX.Element => {
  • )} + */} {isVisibleDealerManagement && (
  • {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */} diff --git a/dictation_client/src/pages/SignupPage/signupInput.tsx b/dictation_client/src/pages/SignupPage/signupInput.tsx index 3f388ce..be83cf7 100644 --- a/dictation_client/src/pages/SignupPage/signupInput.tsx +++ b/dictation_client/src/pages/SignupPage/signupInput.tsx @@ -39,7 +39,11 @@ const SignupInput: React.FC = (): JSX.Element => { const navigate = useNavigate(); const [isPasswordHide, setIsPasswordHide] = useState(true); const [isOpenPolicy, setIsOpenPolicy] = useState(false); - const [isAgreePolicy, setIsAgreePolicy] = useState(false); + const [isOpenPrivacyNotice, setIsOpenPrivacyNoyice] = + useState(false); + const [isCheckedEula, setIsCheckedEula] = useState(false); + const [isCheckedPrivacyNotice, setIsCheckedPrivacyNotice] = + useState(false); const [isPushCreateButton, setIsPushCreateButton] = useState(false); const { hasErrorEmptyAdminName, @@ -90,6 +94,9 @@ const SignupInput: React.FC = (): JSX.Element => { dispatch(getLatestEulaVersionAsync()); }, [dispatch]); + // ボタン押下可否判定ロジック + const canClickButton = () => isCheckedEula && isCheckedPrivacyNotice; + useEffect(() => { // 外部のWebサイトからの遷移時にURLのパラメータを取得 // 以下のようなURLで遷移してきた場合に、Dealerと言語を変更する @@ -371,18 +378,48 @@ const SignupInput: React.FC = (): JSX.Element => { setIsOpenPolicy(true); }} > - {t(getTranslationID("signupPage.label.termsLink"))} + {t(getTranslationID("signupPage.label.linkOfEula"))} - {` ${t(getTranslationID("signupPage.label.termsLinkFor"))} `} + {` ${t(getTranslationID("signupPage.label.forOdms"))} `}
    -
  • )} + {/* ユーザー削除 CCB後回し分なので今は非表示
  • {t( @@ -252,6 +253,7 @@ const UserListPage: React.FC = (): JSX.Element => { )}
  • + */}
{user.name} diff --git a/dictation_client/src/translation/de.json b/dictation_client/src/translation/de.json index 6b1395d..01a37b9 100644 --- a/dictation_client/src/translation/de.json +++ b/dictation_client/src/translation/de.json @@ -73,9 +73,9 @@ "adminName": "Name des Administrators", "email": "E-Mail-Addresse", "password": "Passwort", - "termsLink": "Klicken Sie hier, um die Nutzungsbedingungen zu lesen.", - "termsLinkFor": "für ODMS Cloud.", - "termsCheckBox": "Ja, ich stimme den Nutzungsbedingungen zu.", + "linkOfEula": "Klicken Sie hier, um die Endbenutzer-Lizenzvereinbarung zu lesen.", + "linkOfPrivacyNotice": "Klicken Sie hier, um die Datenschutzerklärung zu lesen.", + "forOdms": "für ODMS Cloud.", "createAccountButton": "Einreichen" } }, diff --git a/dictation_client/src/translation/en.json b/dictation_client/src/translation/en.json index e18c689..c37d4ad 100644 --- a/dictation_client/src/translation/en.json +++ b/dictation_client/src/translation/en.json @@ -73,8 +73,9 @@ "adminName": "Administrator‘s Name", "email": "Email Address", "password": "Password", - "termsLink": "Click here to read the terms of use", - "termsLinkFor": "for OMDS Cloud.", + "linkOfEula": "Click here to read the End User License Agreement.", + "linkOfPrivacyNotice": "Click here to read the Privacy Notice.", + "forOdms": "for ODMS Cloud.", "termsCheckBox": "Yes, I agree to the terms of use.", "createAccountButton": "Submit" } diff --git a/dictation_client/src/translation/es.json b/dictation_client/src/translation/es.json index 36d8ff6..bf3d0f7 100644 --- a/dictation_client/src/translation/es.json +++ b/dictation_client/src/translation/es.json @@ -73,8 +73,9 @@ "adminName": "Nombre del administrador", "email": "Dirección de correo electrónico", "password": "Contraseña", - "termsLink": "Haga clic aquí para leer el término de uso.", - "termsLinkFor": "para la nube ODMS.", + "linkOfEula": "Haga clic aquí para leer el Acuerdo de licencia de usuario final.", + "linkOfPrivacyNotice": "Haga clic aquí para leer el Aviso de Privacidad.", + "forOdms": "para la nube ODMS.", "termsCheckBox": "Sí, estoy de acuerdo con los términos de uso.", "createAccountButton": "Entregar" } diff --git a/dictation_client/src/translation/fr.json b/dictation_client/src/translation/fr.json index b13a338..4c79233 100644 --- a/dictation_client/src/translation/fr.json +++ b/dictation_client/src/translation/fr.json @@ -73,8 +73,9 @@ "adminName": "Nom de l'administrateur", "email": "Adresse e-mail", "password": "Mot de passe", - "termsLink": "Cliquez ici pour lire les conditions d'utilisation.", - "termsLinkFor": "pour ODMS Cloud.", + "linkOfEula": "Cliquez ici pour lire le contrat de licence utilisateur final.", + "linkOfPrivacyNotice": "Cliquez ici pour lire l'avis de confidentialité.", + "forOdms": "pour ODMS Cloud.", "termsCheckBox": "Oui, j'accepte les conditions d'utilisation.", "createAccountButton": "Soumettre" } diff --git a/dictation_function/package-lock.json b/dictation_function/package-lock.json index 4da5447..8ebaf00 100644 --- a/dictation_function/package-lock.json +++ b/dictation_function/package-lock.json @@ -20,8 +20,10 @@ "@types/jest": "^27.5.0", "@types/node": "18.x", "@types/redis": "^2.8.13", + "@types/redis-mock": "^0.17.3", "azure-functions-core-tools": "^4.x", "jest": "^28.0.3", + "redis-mock": "^0.56.3", "rimraf": "^5.0.0", "sqlite3": "^5.1.6", "supertest": "^6.1.3", @@ -2000,6 +2002,15 @@ "@types/node": "*" } }, + "node_modules/@types/redis-mock": { + "version": "0.17.3", + "resolved": "https://registry.npmjs.org/@types/redis-mock/-/redis-mock-0.17.3.tgz", + "integrity": "sha512-1baXyGxRKEDog8p1ReiypODwiST2n3/0pBbgUKEuv9pBXnY6ttRzKATcW5Xz20ZOl9qkKtPIeq20tHgHSdQBAQ==", + "dev": true, + "dependencies": { + "@types/redis": "^2.8.0" + } + }, "node_modules/@types/stack-utils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.2.tgz", @@ -6409,6 +6420,15 @@ "node": ">=4" } }, + "node_modules/redis-mock": { + "version": "0.56.3", + "resolved": "https://registry.npmjs.org/redis-mock/-/redis-mock-0.56.3.tgz", + "integrity": "sha512-ynaJhqk0Qf3Qajnwvy4aOjS4Mdf9IBkELWtjd+NYhpiqu4QCNq6Vf3Q7c++XRPGiKiwRj9HWr0crcwy7EiPjYQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/redis-parser": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", diff --git a/dictation_function/package.json b/dictation_function/package.json index 8a6260d..33f2287 100644 --- a/dictation_function/package.json +++ b/dictation_function/package.json @@ -25,8 +25,10 @@ "@types/jest": "^27.5.0", "@types/node": "18.x", "@types/redis": "^2.8.13", + "@types/redis-mock": "^0.17.3", "azure-functions-core-tools": "^4.x", "jest": "^28.0.3", + "redis-mock": "^0.56.3", "rimraf": "^5.0.0", "sqlite3": "^5.1.6", "supertest": "^6.1.3", diff --git a/dictation_function/src/functions/licenseAlert.ts b/dictation_function/src/functions/licenseAlert.ts index bbab8b0..ad4119f 100644 --- a/dictation_function/src/functions/licenseAlert.ts +++ b/dictation_function/src/functions/licenseAlert.ts @@ -77,7 +77,7 @@ export async function licenseAlertProcessing( const keys = await keysAsync(`${SEND_COMPLETE_PREFIX}${formattedDate}*`); console.log(`delete terget:${keys}`); if (keys.length > 0) { - const delResult = await delAsync(...keys); + const delResult = await delAsync(keys); console.log(`delete number:${delResult}`); } } catch (e) { diff --git a/dictation_function/src/test/licenseAlert.spec.ts b/dictation_function/src/test/licenseAlert.spec.ts index 390dc18..c3891a0 100644 --- a/dictation_function/src/test/licenseAlert.spec.ts +++ b/dictation_function/src/test/licenseAlert.spec.ts @@ -13,13 +13,15 @@ import { ADB2C_SIGN_IN_TYPE } from "../constants"; import { SendGridService } from "../sendgrid/sendgrid"; import { AdB2cService } from "../adb2c/adb2c"; import { InvocationContext } from "@azure/functions"; -import { RedisClient } from "redis"; -import { createRedisClient } from "../redis/redis"; +import { RedisClient, createClient } from "redis-mock"; +import { promisify } from "util"; describe("licenseAlert", () => { dotenv.config({ path: ".env" }); dotenv.config({ path: ".env.local", override: true }); let source: DataSource | null = null; + const redisClient = createClient(); + beforeEach(async () => { source = new DataSource({ type: "sqlite", @@ -35,17 +37,15 @@ describe("licenseAlert", () => { if (!source) return; await source.destroy(); source = null; + //licenseAlertProcessingの処理の最後にキャッシュ削除処理があるため、ここでクリーンアップは行わない }); - it("テストを通すための仮", async () => {}); - /* - it("ライセンス在庫不足メールが送信され、ライセンス失効警告メールが送信されないこと", async () => { if (!source) fail(); const context = new InvocationContext(); const sendgridMock = new SendGridServiceMock() as SendGridService; const adb2cMock = new AdB2cServiceMock() as AdB2cService; - const redisClient = createRedisClient(); + // 呼び出し回数でテスト成否を判定 const spySend = jest.spyOn(sendgridMock, "sendMail"); @@ -77,7 +77,10 @@ describe("licenseAlert", () => { adb2cMock ); expect(spySend.mock.calls).toHaveLength(1); - redisClient.quit; + // redisからキャッシュが削除されていることを確認 + const getAsync = promisify(redisClient.keys).bind(redisClient); + const keys = await getAsync(`*`); + expect(keys).toHaveLength(0); }); it("ライセンス在庫不足メール、ライセンス失効警告メールが送信されること", async () => { @@ -85,7 +88,6 @@ describe("licenseAlert", () => { const context = new InvocationContext(); const sendgridMock = new SendGridServiceMock() as SendGridService; const adb2cMock = new AdB2cServiceMock() as AdB2cService; - const redisClient = createRedisClient(); // 呼び出し回数でテスト成否を判定 const spySend = jest.spyOn(sendgridMock, "sendMail"); @@ -118,7 +120,10 @@ describe("licenseAlert", () => { adb2cMock ); expect(spySend.mock.calls).toHaveLength(2); - redisClient.quit; + // redisからキャッシュが削除されていることを確認 + const getAsync = promisify(redisClient.keys).bind(redisClient); + const keys = await getAsync(`*`); + expect(keys).toHaveLength(0); }); it("在庫があるため、ライセンス在庫不足メールが送信されないこと", async () => { @@ -126,7 +131,6 @@ describe("licenseAlert", () => { const context = new InvocationContext(); const sendgridMock = new SendGridServiceMock() as SendGridService; const adb2cMock = new AdB2cServiceMock() as AdB2cService; - const redisClient = createRedisClient(); // 呼び出し回数でテスト成否を判定 const spySend = jest.spyOn(sendgridMock, "sendMail"); @@ -172,7 +176,10 @@ describe("licenseAlert", () => { adb2cMock ); expect(spySend.mock.calls).toHaveLength(0); - redisClient.quit; + // redisからキャッシュが削除されていることを確認 + const getAsync = promisify(redisClient.keys).bind(redisClient); + const keys = await getAsync(`*`); + expect(keys).toHaveLength(0); }); it("AutoRenewがtureのため、ライセンス失効警告メールが送信されないこと", async () => { @@ -180,7 +187,6 @@ describe("licenseAlert", () => { const context = new InvocationContext(); const sendgridMock = new SendGridServiceMock() as SendGridService; const adb2cMock = new AdB2cServiceMock() as AdB2cService; - const redisClient = createRedisClient(); // 呼び出し回数でテスト成否を判定 const spySend = jest.spyOn(sendgridMock, "sendMail"); @@ -213,9 +219,11 @@ describe("licenseAlert", () => { adb2cMock ); expect(spySend.mock.calls).toHaveLength(1); - redisClient.quit; + // redisからキャッシュが削除されていることを確認 + const getAsync = promisify(redisClient.keys).bind(redisClient); + const keys = await getAsync(`*`); + expect(keys).toHaveLength(0); }); - */ }); // テスト用sendgrid diff --git a/dictation_function/src/test/licenseAutoAllocation.spec.ts b/dictation_function/src/test/licenseAutoAllocation.spec.ts index 170862a..d0bf9ef 100644 --- a/dictation_function/src/test/licenseAutoAllocation.spec.ts +++ b/dictation_function/src/test/licenseAutoAllocation.spec.ts @@ -282,13 +282,9 @@ describe("licenseAutoAllocation", () => { it("有効期限が指定日のライセンスが自動更新されること(リトライ用)", async () => { if (!source) fail(); const context = new InvocationContext(); - // 11/22の日付を作成 - const dateSeptember22 = new Date(); - dateSeptember22.setMonth(11); - dateSeptember22.setDate(22); - dateSeptember22.setHours(23, 59, 59); - const currentDateEndTime = new DateWithDayEndTime(dateSeptember22); - + // 2023/11/22の日付を作成 + const date1122 = new Date(2023, 10, 22, 23, 59, 59); + const currentDateEndTime = new DateWithDayEndTime(date1122); // アカウント const account1 = await makeTestAccount( source, @@ -328,7 +324,7 @@ describe("licenseAutoAllocation", () => { auto_renew: false, }); - // 割り当て済みで有効期限が12/31のライセンス + // 割り当て済みで有効期限が11/22のライセンス await createLicense( source, 1, @@ -389,10 +385,9 @@ describe("licenseAutoAllocation", () => { null, null ); - // 割り当て済みの更新対象ではないライセンス const nextDate = new Date(); - nextDate.setDate(dateSeptember22.getDate() + 1); + nextDate.setDate(date1122.getDate() + 1); nextDate.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定 nextDate.setMilliseconds(0); await createLicense( @@ -407,15 +402,13 @@ describe("licenseAutoAllocation", () => { null, null ); - // 有効期限が先の未割当ライセンスを作成 // idが100のものは有効期限が当日なので自動割り当て対象外 // idが101のものから割り当てられる for (let i = 0; i < 10; i++) { - const date = new Date(); - date.setDate(dateSeptember22.getDate() + i); - date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定 - date.setMilliseconds(0); + // 2023/11/22の日付を作成 + const date = new Date(2023, 10, 22, 23, 59, 59); + date.setDate(date.getDate() + i); await createLicense( source, i + 100, @@ -446,8 +439,7 @@ describe("licenseAutoAllocation", () => { null, null ); - - await licenseAutoAllocationProcessing(context, source, dateSeptember22); + await licenseAutoAllocationProcessing(context, source, date1122); const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id); const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id); const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id); diff --git a/dictation_server/.devcontainer/docker-compose.yml b/dictation_server/.devcontainer/docker-compose.yml index 47cf396..ca3a9a8 100644 --- a/dictation_server/.devcontainer/docker-compose.yml +++ b/dictation_server/.devcontainer/docker-compose.yml @@ -2,6 +2,7 @@ version: '3' services: dictation_server: + container_name: dictation_server_dev_container env_file: ../.env build: . working_dir: /app/dictation_server @@ -16,6 +17,15 @@ services: - CHOKIDAR_USEPOLLING=true networks: - external + test_mysql_db: + image: mysql:8.0-bullseye + environment: + MYSQL_ROOT_PASSWORD: root_password + MYSQL_DATABASE: odms + MYSQL_USER: user + MYSQL_PASSWORD: password + networks: + - external networks: external: name: omds_network diff --git a/dictation_server/.devcontainer/pipeline-docker-compose.yml b/dictation_server/.devcontainer/pipeline-docker-compose.yml new file mode 100644 index 0000000..19708a2 --- /dev/null +++ b/dictation_server/.devcontainer/pipeline-docker-compose.yml @@ -0,0 +1,35 @@ +version: '3' + +services: + dictation_server: + container_name: dictation_server_dev_container + env_file: ../.env + build: . + working_dir: /app/dictation_server + ports: + - '8081:8081' + volumes: + - ../../:/app + - node_modules:/app/dictation_server/node_modules + expose: + - '8081' + environment: + - CHOKIDAR_USEPOLLING=true + depends_on: + - test_mysql_db + networks: + - network + test_mysql_db: + image: mysql:8.0-bullseye + environment: + MYSQL_ROOT_PASSWORD: root_password + MYSQL_DATABASE: odms + MYSQL_USER: user + MYSQL_PASSWORD: password + networks: + - network +networks: + network: + name: test_network +volumes: + node_modules: \ No newline at end of file diff --git a/dictation_server/.env.test b/dictation_server/.env.test new file mode 100644 index 0000000..a2748df --- /dev/null +++ b/dictation_server/.env.test @@ -0,0 +1,37 @@ +STAGE=local +NO_COLOR=TRUE +CORS=TRUE +PORT=8081 +TENANT_NAME=xxxxxxxxxx +SIGNIN_FLOW_NAME=b2c_1_signin_xxx +ADB2C_TENANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +ADB2C_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx +ADB2C_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +ADB2C_ORIGIN=https://example.com/xxxxxxxxx.onmicrosoft.com/b2c_1_signin_xxx/ +KEY_VAULT_NAME=xxxxxxxxxxxx +JWT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEA5IZZNgDew9eGmuFTezwdHYLSaJvUPPIKYoiOeVLD1paWNI51\n7Vkaoh0ngprcKOdv6T1N07V4igK7mOim2zY3yCTR6wcWR3PfFJrl9vh5SOo79koZ\noJb27YiM4jtxfx2dezzp0T2GoNR5rRolPUbWFJXnDe0DVXYXpJLb4LAlF2XAyYX0\nSYKUVUsJnzm5k4xbXtnwPwVbpm0EdswBE6qSfiL9zWk9dvHoKzSnfSDzDFoFcEoV\nchawzYXf/MM1YR4wo5XyzECc6Q5Ah4z522//mBNNaDHv83Yuw3mGShT73iJ0JQdk\nTturshv2Ecma38r6ftrIwNYXw4VVatJM8+GOOQIDAQABAoIBADrwp7u097+dK/tw\nWD61n3DIGAqg/lmFt8X4IH8MKLSE/FKr16CS1bqwOEuIM3ZdUtDeXd9Xs7IsyEPE\n5ZwuXK7DSF0M4+Mj8Ip49Q0Aww9aUoLQU9HGfgN/r4599GTrt31clZXA/6Mlighq\ncOZgCcEfdItz8OMu5SQuOIW4CKkCuaWnPOP26UqZocaXNZfpZH0iFLATMMH/TT8x\nay9ToHTQYE17ijdQ/EOLSwoeDV1CU1CIE3P4YfLJjvpKptly5dTevriHEzBi70Jx\n/KEPUn9Jj2gZafrUxRVhmMbm1zkeYxL3gsqRuTzRjEeeILuZhSJyCkQZyUNARxsg\nQY4DZfECgYEA+YLKUtmYTx60FS6DJ4s31TAsXY8kwhq/lB9E3GBZKDd0DPayXEeK\n4UWRQDTT6MI6fedW69FOZJ5sFLp8HQpcssb4Weq9PCpDhNTx8MCbdH3Um5QR3vfW\naKq/1XM8MDUnx5XcNYd87Aw3azvJAvOPr69as8IPnj6sKaRR9uQjbYUCgYEA6nfV\n5j0qmn0EJXZJblk4mvvjLLoWSs17j9YlrZJlJxXMDFRYtgnelv73xMxOMvcGoxn5\nifs7dpaM2x5EmA6jVU5sYaB/beZGEPWqPYGyjIwXPvUGAAv8Gbnvpp+xlSco/Dum\nIq0w+43ry5/xWh6CjfrvKV0J2bDOiJwPEdu/8iUCgYEAnBBSvL+dpN9vhFAzeOh7\nY71eAqcmNsLEUcG9MJqTKbSFwhYMOewF0iHRWHeylEPokhfBJn8kqYrtz4lVWFTC\n5o/Nh3BsLNXCpbMMIapXkeWiti1HgE9ErPMgSkJpwz18RDpYIqM8X+jEQS6D7HSr\nyxfDg+w+GJza0rEVE3hfMIECgYBw+KZ2VfhmEWBjEHhXE+QjQMR3s320MwebCUqE\nNCpKx8TWF/naVC0MwfLtvqbbBY0MHyLN6d//xpA9r3rLbRojqzKrY2KiuDYAS+3n\nzssRzxoQOozWju+8EYu30/ADdqfXyIHG6X3VZs87AGiQzGyJLmP3oR1y5y7MQa09\nJI16hQKBgHK5uwJhGa281Oo5/FwQ3uYLymbNwSGrsOJXiEu2XwJEXwVi2ELOKh4/\n03pBk3Kva3fIwEK+vCzDNnxShIQqBE76/2I1K1whOfoUehhYvKHGaXl2j70Zz9Ks\nrkGW1cx7p+yDqATDrwHBHTHFh5bUTTn8dN40n0e0W/llurpbBkJM\n-----END RSA PRIVATE KEY-----\n" +JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5IZZNgDew9eGmuFTezwd\nHYLSaJvUPPIKYoiOeVLD1paWNI517Vkaoh0ngprcKOdv6T1N07V4igK7mOim2zY3\nyCTR6wcWR3PfFJrl9vh5SOo79koZoJb27YiM4jtxfx2dezzp0T2GoNR5rRolPUbW\nFJXnDe0DVXYXpJLb4LAlF2XAyYX0SYKUVUsJnzm5k4xbXtnwPwVbpm0EdswBE6qS\nfiL9zWk9dvHoKzSnfSDzDFoFcEoVchawzYXf/MM1YR4wo5XyzECc6Q5Ah4z522//\nmBNNaDHv83Yuw3mGShT73iJ0JQdkTturshv2Ecma38r6ftrIwNYXw4VVatJM8+GO\nOQIDAQAB\n-----END PUBLIC KEY-----\n" +SENDGRID_API_KEY=SG.P_xxxx_xxxxxxxxxxxxxxx.xxxxxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxx-pc +MAIL_FROM=noreply@example.com +NOTIFICATION_HUB_NAME=ntf-odms-dev +NOTIFICATION_HUB_CONNECT_STRING=Endpoint=sb://example.com/;SharedAccessKeyName=DefaultFullSharedAccessSignature;SharedAccessKey=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX= +APP_DOMAIN=http://localhost:8081/ +STORAGE_TOKEN_EXPIRE_TIME=30 +STORAGE_ACCOUNT_NAME_US=saxxxxusxxx +STORAGE_ACCOUNT_NAME_AU=saxxxxauxxx +STORAGE_ACCOUNT_NAME_EU=saxxxxeuxxx +STORAGE_ACCOUNT_KEY_US=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX== +STORAGE_ACCOUNT_KEY_AU=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX== +STORAGE_ACCOUNT_KEY_EU=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX== +STORAGE_ACCOUNT_ENDPOINT_US=https://xxxxxxxxxxxx.blob.core.windows.net/ +STORAGE_ACCOUNT_ENDPOINT_AU=https://xxxxxxxxxxxx.blob.core.windows.net/ +STORAGE_ACCOUNT_ENDPOINT_EU=https://xxxxxxxxxxxx.blob.core.windows.net/ +ACCESS_TOKEN_LIFETIME_WEB=7200000 +REFRESH_TOKEN_LIFETIME_WEB=86400000 +REFRESH_TOKEN_LIFETIME_DEFAULT=2592000000 +EMAIL_CONFIRM_LIFETIME=86400000 +REDIS_HOST=redis-cache +REDIS_PORT=6379 +REDIS_PASSWORD=omdsredispass +ADB2C_CACHE_TTL=86400 +TEMPLATE_ROOT=dist \ No newline at end of file diff --git a/dictation_server/db/dbconfig.yml b/dictation_server/db/dbconfig.yml index c7ef32d..38bd96b 100644 --- a/dictation_server/db/dbconfig.yml +++ b/dictation_server/db/dbconfig.yml @@ -10,3 +10,7 @@ ci: dialect: mysql dir: ./dictation_server/db/migrations datasource: DB_USERNAME:DB_PASS@tcp(DB_HOST:DB_PORT)/DB_NAME?charset=utf8mb4&collation=utf8mb4_0900_ai_ci&parseTime=true +test: + dialect: mysql + dir: /app/dictation_server/db/migrations + datasource: user:password@tcp(test_mysql_db:3306)/odms?charset=utf8mb4&collation=utf8mb4_0900_ai_ci&parseTime=true \ No newline at end of file diff --git a/dictation_server/db/migrations/053-delete-license-alert.sql b/dictation_server/db/migrations/053-delete-license-alert.sql new file mode 100644 index 0000000..e7e6871 --- /dev/null +++ b/dictation_server/db/migrations/053-delete-license-alert.sql @@ -0,0 +1,6 @@ +-- +migrate Up +ALTER TABLE `users_archive` DROP COLUMN `license_alert`; + + +-- +migrate Down +ALTER TABLE `users_archive` ADD COLUMN `license_alert` BOOLEAN NOT NULL COMMENT 'ライセンスの期限切れ通知をするかどうか'; \ No newline at end of file diff --git a/dictation_server/package.json b/dictation_server/package.json index d92d3d4..a8d50bd 100644 --- a/dictation_server/package.json +++ b/dictation_server/package.json @@ -17,7 +17,7 @@ "tc": "tsc --noEmit", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\"", "lint:fix": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest", + "test": "jest -w 1", "test:watch": "jest --watch", "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", @@ -26,7 +26,8 @@ "openapi-format": "cat \"src/api/odms/openapi.json\" | jq -c . > \"src/api/odms/openapi.json\" && prettier --write \"src/api/odms/*.json\"", "migrate:up": "sql-migrate up -config=/app/dictation_server/db/dbconfig.yml -env=ccb", "migrate:down": "sql-migrate down -config=/app/dictation_server/db/dbconfig.yml -env=ccb", - "migrate:status": "sql-migrate status -config=/app/dictation_server/db/dbconfig.yml -env=ccb" + "migrate:status": "sql-migrate status -config=/app/dictation_server/db/dbconfig.yml -env=ccb", + "migrate:up:test": "sql-migrate up -config=/app/dictation_server/db/dbconfig.yml -env=test" }, "dependencies": { "@azure/identity": "^3.1.3", diff --git a/dictation_server/src/common/test/init.ts b/dictation_server/src/common/test/init.ts new file mode 100644 index 0000000..e919d16 --- /dev/null +++ b/dictation_server/src/common/test/init.ts @@ -0,0 +1,21 @@ +import { DataSource } from "typeorm"; + +export const truncateAllTable = async (source: DataSource) => { + const entities = source.entityMetadatas; + const queryRunner = source.createQueryRunner(); + + try { + await queryRunner.startTransaction(); + await queryRunner.query('SET FOREIGN_KEY_CHECKS=0'); + for (const entity of entities) { + await queryRunner.query(`TRUNCATE TABLE \`${entity.tableName}\``); + } + await queryRunner.query('SET FOREIGN_KEY_CHECKS=1'); + await queryRunner.commitTransaction(); + } catch (err) { + await queryRunner.rollbackTransaction(); + throw err; + } finally { + await queryRunner.release(); + } +}; \ No newline at end of file diff --git a/dictation_server/src/common/test/modules.ts b/dictation_server/src/common/test/modules.ts index 61c0396..7c51a50 100644 --- a/dictation_server/src/common/test/modules.ts +++ b/dictation_server/src/common/test/modules.ts @@ -48,7 +48,7 @@ export const makeTestingModule = async ( const module: TestingModule = await Test.createTestingModule({ imports: [ ConfigModule.forRoot({ - envFilePath: ['.env.local', '.env'], + envFilePath: ['.env.test', '.env'], isGlobal: true, }), AuthModule, diff --git a/dictation_server/src/constants/index.ts b/dictation_server/src/constants/index.ts index da502d7..511e4b8 100644 --- a/dictation_server/src/constants/index.ts +++ b/dictation_server/src/constants/index.ts @@ -88,6 +88,16 @@ export const USER_ROLES = { TYPIST: 'typist', } as const; +/** + * ロールのソート順 + * @const {string[]} + */ +export const USER_ROLE_ORDERS = [ + USER_ROLES.AUTHOR, + USER_ROLES.TYPIST, + USER_ROLES.NONE, +] as string[]; + /** * ライセンス注文状態 * @const {string[]} diff --git a/dictation_server/src/features/accounts/accounts.controller.spec.ts b/dictation_server/src/features/accounts/accounts.controller.spec.ts index 2761b0e..f69ece1 100644 --- a/dictation_server/src/features/accounts/accounts.controller.spec.ts +++ b/dictation_server/src/features/accounts/accounts.controller.spec.ts @@ -13,7 +13,7 @@ describe('AccountsController', () => { const module: TestingModule = await Test.createTestingModule({ imports: [ ConfigModule.forRoot({ - envFilePath: ['.env.local', '.env'], + envFilePath: ['.env.test', '.env'], isGlobal: true, }), ], diff --git a/dictation_server/src/features/accounts/accounts.service.spec.ts b/dictation_server/src/features/accounts/accounts.service.spec.ts index 9b192c9..4bfd687 100644 --- a/dictation_server/src/features/accounts/accounts.service.spec.ts +++ b/dictation_server/src/features/accounts/accounts.service.spec.ts @@ -78,23 +78,36 @@ import { AccountsRepositoryService } from '../../repositories/accounts/accounts. import { UsersRepositoryService } from '../../repositories/users/users.repository.service'; import { createWorkflow, getWorkflows } from '../workflows/test/utility'; import { UsersService } from '../users/users.service'; +import { truncateAllTable } from '../../common/test/init'; describe('createAccount', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('アカウントを作成できる', async () => { @@ -754,20 +767,32 @@ describe('createAccount', () => { describe('createPartnerAccount', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -1740,20 +1765,32 @@ describe('AccountsService', () => { describe('getLicenseSummary', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('第五階層のライセンス情報を取得する', async () => { @@ -1787,7 +1824,7 @@ describe('getLicenseSummary', () => { // 有効期限が14日後のライセンスを追加(5ライセンス) const expiryDate = new Date(); expiryDate.setDate(expiryDate.getDate() + 14); - expiryDate.setHours(23, 59, 59, 999); + expiryDate.setHours(23, 59, 59, 0); for (let i = 0; i < 5; i++) { await createLicenseSetExpiryDateAndStatus( source, @@ -1812,7 +1849,7 @@ describe('getLicenseSummary', () => { await createLicenseSetExpiryDateAndStatus( source, childAccountId1, - new Date(2500, 1, 1, 23, 59, 59), + new Date(2037, 1, 1, 23, 59, 59), element, 1, ); @@ -1823,7 +1860,7 @@ describe('getLicenseSummary', () => { await createLicenseSetExpiryDateAndStatus( source, childAccountId2, - new Date(2500, 1, 1, 23, 59, 59), + new Date(2037, 1, 1, 23, 59, 59), 'Unallocated', ); } @@ -1887,20 +1924,32 @@ describe('getLicenseSummary', () => { describe('getPartnerAccount', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -1942,7 +1991,7 @@ describe('getPartnerAccount', () => { // 有効期限が14日後のライセンスを追加(5ライセンス) const expiryDate = new Date(); expiryDate.setDate(expiryDate.getDate() + 14); - expiryDate.setHours(23, 59, 59, 999); + expiryDate.setHours(23, 59, 59, 0); for (let i = 0; i < 5; i++) { await createLicenseSetExpiryDateAndStatus( source, @@ -1960,7 +2009,7 @@ describe('getPartnerAccount', () => { // 有効期限が15日後のライセンスを追加 expiryDate.setDate(expiryDate.getDate() + 15); - expiryDate.setHours(23, 59, 59, 999); + expiryDate.setHours(23, 59, 59, 0); await createLicenseSetExpiryDateAndStatus( source, childAccountId3, @@ -1975,7 +2024,7 @@ describe('getPartnerAccount', () => { await createLicenseSetExpiryDateAndStatus( source, childAccountId1, - new Date(2500, 1, 1, 23, 59, 59), + new Date(2037, 1, 1, 23, 59, 59), element, ); }); @@ -1985,7 +2034,7 @@ describe('getPartnerAccount', () => { await createLicenseSetExpiryDateAndStatus( source, childAccountId2, - new Date(2500, 1, 1, 23, 59, 59), + new Date(2037, 1, 1, 23, 59, 59), 'Unallocated', ); } @@ -2029,20 +2078,32 @@ describe('getPartnerAccount', () => { describe('getOrderHistories', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -2161,20 +2222,32 @@ describe('getOrderHistories', () => { describe('issueLicense', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -2468,20 +2541,32 @@ describe('issueLicense', () => { describe('getDealers', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('Dealerを取得できる', async () => { @@ -2551,20 +2636,32 @@ describe('getDealers', () => { describe('createTypistGroup', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('TypistGroupを作成できる', async () => { @@ -2851,20 +2948,32 @@ describe('createTypistGroup', () => { describe('getTypistGroup', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('指定したIDのTypistGroupを取得できる', async () => { @@ -3051,20 +3160,32 @@ describe('getTypistGroup', () => { describe('updateTypistGroup', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('TypistGroupを更新できる', async () => { @@ -3467,20 +3588,32 @@ describe('updateTypistGroup', () => { describe('getWorktypes', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -3587,20 +3720,32 @@ describe('getWorktypes', () => { describe('createWorktype', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -3740,20 +3885,32 @@ describe('createWorktype', () => { describe('updateWorktype', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -4019,20 +4176,32 @@ describe('updateWorktype', () => { describe('deleteWorktype', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -4267,20 +4436,32 @@ describe('deleteWorktype', () => { describe('getOptionItems', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -4406,20 +4587,32 @@ describe('getOptionItems', () => { describe('updateOptionItems', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -4732,20 +4925,32 @@ describe('updateOptionItems', () => { describe('updateActiveWorktype', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -4960,20 +5165,32 @@ describe('updateActiveWorktype', () => { describe('ライセンス発行キャンセル', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('ライセンス発行のキャンセルが完了する(第一階層で実行)', async () => { @@ -5274,20 +5491,32 @@ describe('ライセンス発行キャンセル', () => { describe('パートナー一覧取得', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('パートナー一覧を取得する', async () => { @@ -5442,20 +5671,32 @@ describe('パートナー一覧取得', () => { describe('アカウント情報更新', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('アカウント情報を更新する(第五階層が実行/セカンダリ管理者ユーザがnull)', async () => { @@ -5660,20 +5901,32 @@ describe('アカウント情報更新', () => { describe('getAccountInfo', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('パラメータのユーザに対応するアカウント情報を取得できる', async () => { @@ -5720,20 +5973,32 @@ describe('getAccountInfo', () => { }); describe('getAuthors', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('アカウント内のAuthorユーザーの一覧を取得できる', async () => { @@ -5872,20 +6137,32 @@ describe('getAuthors', () => { describe('getTypists', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('アカウント内のTypistユーザーの一覧を取得できる', async () => { @@ -6054,20 +6331,32 @@ describe('getTypists', () => { describe('deleteAccountAndData', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('アカウント情報が削除されること', async () => { @@ -6450,20 +6739,32 @@ describe('deleteAccountAndData', () => { }); describe('getAccountInfoMinimalAccess', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('IDトークンのsub情報からアカウントの階層情報を取得できること(第五階層)', async () => { @@ -6583,20 +6884,32 @@ describe('getAccountInfoMinimalAccess', () => { }); describe('getCompanyName', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); diff --git a/dictation_server/src/features/auth/auth.controller.spec.ts b/dictation_server/src/features/auth/auth.controller.spec.ts index 93a7999..a762b74 100644 --- a/dictation_server/src/features/auth/auth.controller.spec.ts +++ b/dictation_server/src/features/auth/auth.controller.spec.ts @@ -14,7 +14,7 @@ describe('AuthController', () => { const module: TestingModule = await Test.createTestingModule({ imports: [ ConfigModule.forRoot({ - envFilePath: ['.env.local', '.env'], + envFilePath: ['.env.test', '.env'], isGlobal: true, }), ], diff --git a/dictation_server/src/features/auth/auth.service.spec.ts b/dictation_server/src/features/auth/auth.service.spec.ts index 856f719..c0b47d4 100644 --- a/dictation_server/src/features/auth/auth.service.spec.ts +++ b/dictation_server/src/features/auth/auth.service.spec.ts @@ -20,6 +20,7 @@ import { v4 as uuidv4 } from 'uuid'; import { TIERS, USER_ROLES } from '../../constants'; import { decode, isVerifyError } from '../../common/jwt'; import { RefreshToken, AccessToken } from '../../common/token'; +import { truncateAllTable } from '../../common/test/init'; describe('AuthService', () => { it('IDトークンの検証とペイロードの取得に成功する', async () => { @@ -162,20 +163,32 @@ describe('AuthService', () => { describe('checkIsAcceptedLatestVersion', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('同意済み利用規約バージョンが最新のときにチェックが通ること(第五)', async () => { @@ -325,20 +338,32 @@ describe('checkIsAcceptedLatestVersion', () => { describe('generateDelegationRefreshToken', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('代行操作が許可されたパートナーの代行操作用リフレッシュトークンを取得できること', async () => { @@ -459,20 +484,32 @@ describe('generateDelegationRefreshToken', () => { describe('generateDelegationAccessToken', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('代行操作用リフレッシュトークンから代行操作用アクセストークンを取得できること', async () => { @@ -558,20 +595,32 @@ describe('generateDelegationAccessToken', () => { describe('updateDelegationAccessToken', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -743,7 +792,7 @@ describe('updateDelegationAccessToken', () => { } // 代行操作対象アカウントを削除 - deleteAccount(source, partnerAccount.id); + await deleteAccount(source, partnerAccount.id); try { await service.updateDelegationAccessToken( diff --git a/dictation_server/src/features/files/files.controller.spec.ts b/dictation_server/src/features/files/files.controller.spec.ts index 9b7eb76..3986b38 100644 --- a/dictation_server/src/features/files/files.controller.spec.ts +++ b/dictation_server/src/features/files/files.controller.spec.ts @@ -10,7 +10,7 @@ describe('FilesController', () => { const module: TestingModule = await Test.createTestingModule({ imports: [ ConfigModule.forRoot({ - envFilePath: ['.env.local', '.env'], + envFilePath: ['.env.test', '.env'], isGlobal: true, }), ], diff --git a/dictation_server/src/features/files/files.service.spec.ts b/dictation_server/src/features/files/files.service.spec.ts index 89c9f68..62e4286 100644 --- a/dictation_server/src/features/files/files.service.spec.ts +++ b/dictation_server/src/features/files/files.service.spec.ts @@ -40,23 +40,36 @@ import { TASK_STATUS, USER_ROLES, } from '../../constants'; +import { truncateAllTable } from '../../common/test/init'; describe('publishUploadSas', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -287,20 +300,32 @@ describe('publishUploadSas', () => { describe('タスク作成から自動ルーティング(DB使用)', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('タスク作成時に、自動ルーティングを行うことができる(APIの引数として渡されたAuthorIDとworkType)', async () => { @@ -997,20 +1022,32 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { describe('音声ファイルダウンロードURL取得', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -1490,20 +1527,32 @@ describe('音声ファイルダウンロードURL取得', () => { describe('テンプレートファイルダウンロードURL取得', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -1933,20 +1982,32 @@ describe('テンプレートファイルダウンロードURL取得', () => { describe('publishTemplateFileUploadSas', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -2036,20 +2097,32 @@ describe('publishTemplateFileUploadSas', () => { describe('templateUploadFinished', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); diff --git a/dictation_server/src/features/files/test/utility.ts b/dictation_server/src/features/files/test/utility.ts index 12bb21c..1fe086b 100644 --- a/dictation_server/src/features/files/test/utility.ts +++ b/dictation_server/src/features/files/test/utility.ts @@ -152,7 +152,7 @@ export const makeTestingModuleWithBlobAndNotification = async ( const module: TestingModule = await Test.createTestingModule({ imports: [ ConfigModule.forRoot({ - envFilePath: ['.env.local', '.env'], + envFilePath: ['.env.test', '.env'], isGlobal: true, }), AuthModule, diff --git a/dictation_server/src/features/licenses/licenses.controller.spec.ts b/dictation_server/src/features/licenses/licenses.controller.spec.ts index 70daf6c..1b3fdc4 100644 --- a/dictation_server/src/features/licenses/licenses.controller.spec.ts +++ b/dictation_server/src/features/licenses/licenses.controller.spec.ts @@ -11,7 +11,7 @@ describe('LicensesController', () => { const module: TestingModule = await Test.createTestingModule({ imports: [ ConfigModule.forRoot({ - envFilePath: ['.env.local', '.env'], + envFilePath: ['.env.test', '.env'], isGlobal: true, }), ], diff --git a/dictation_server/src/features/licenses/licenses.service.spec.ts b/dictation_server/src/features/licenses/licenses.service.spec.ts index 1a5b023..63db663 100644 --- a/dictation_server/src/features/licenses/licenses.service.spec.ts +++ b/dictation_server/src/features/licenses/licenses.service.spec.ts @@ -26,23 +26,36 @@ import { } from '../../common/test/utility'; import { LicensesRepositoryService } from '../../repositories/licenses/licenses.repository.service'; import { overrideSendgridService } from '../../common/test/overrides'; +import { truncateAllTable } from '../../common/test/init'; describe('ライセンス注文', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -190,20 +203,32 @@ describe('ライセンス注文', () => { describe('カードライセンス発行', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -268,20 +293,32 @@ describe('カードライセンス発行', () => { describe('カードライセンスを取り込む', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); it('カードライセンス取り込みが完了する', async () => { @@ -601,20 +638,32 @@ describe('カードライセンスを取り込む', () => { describe('ライセンス割り当て', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -702,6 +751,7 @@ describe('ライセンス割り当て', () => { }); const date = new Date(); date.setDate(date.getDate() + 30); + date.setMilliseconds(0); await createLicense( source, 1, @@ -768,6 +818,8 @@ describe('ライセンス割り当て', () => { }); const date = new Date(); date.setDate(date.getDate() + 30); + date.setMilliseconds(0); + await createLicense( source, 1, @@ -874,6 +926,7 @@ describe('ライセンス割り当て', () => { }); const date = new Date(); date.setDate(date.getDate() + 30); + date.setMilliseconds(0); await createLicense( source, 1, @@ -939,6 +992,7 @@ describe('ライセンス割り当て', () => { }); const date = new Date(); date.setDate(date.getDate() + 30); + date.setMilliseconds(0); await createLicense( source, 1, @@ -1004,6 +1058,7 @@ describe('ライセンス割り当て', () => { }); const date = new Date(); date.setDate(date.getDate() + 30); + date.setMilliseconds(0); await createLicense( source, 1, @@ -1069,6 +1124,7 @@ describe('ライセンス割り当て', () => { }); const date = new Date(); date.setDate(date.getDate() - 30); + date.setMilliseconds(0); await createLicense( source, 1, @@ -1110,6 +1166,7 @@ describe('ライセンス割り当て', () => { }); const date = new Date(); date.setDate(date.getDate() + 30); + date.setMilliseconds(0); await createLicense( source, 1, @@ -1161,20 +1218,32 @@ describe('ライセンス割り当て', () => { describe('ライセンス割り当て解除', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -1192,6 +1261,7 @@ describe('ライセンス割り当て解除', () => { }); const date = new Date(); date.setDate(date.getDate() + 30); + date.setMilliseconds(0); await createLicense( source, 1, @@ -1271,6 +1341,7 @@ describe('ライセンス割り当て解除', () => { }); const date = new Date(); date.setDate(date.getDate() + 30); + date.setMilliseconds(0); await createLicense( source, 1, @@ -1316,20 +1387,32 @@ describe('ライセンス割り当て解除', () => { describe('ライセンス注文キャンセル', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); diff --git a/dictation_server/src/features/licenses/test/utility.ts b/dictation_server/src/features/licenses/test/utility.ts index 321f9c0..3dfdda3 100644 --- a/dictation_server/src/features/licenses/test/utility.ts +++ b/dictation_server/src/features/licenses/test/utility.ts @@ -172,6 +172,7 @@ export const selectLicenseAllocationHistory = async ( license_id: licence_id, }, order: { + id: 'DESC', executed_at: 'DESC', }, }); diff --git a/dictation_server/src/features/notification/notification.controller.spec.ts b/dictation_server/src/features/notification/notification.controller.spec.ts index fc14685..bbf71a7 100644 --- a/dictation_server/src/features/notification/notification.controller.spec.ts +++ b/dictation_server/src/features/notification/notification.controller.spec.ts @@ -10,7 +10,7 @@ describe('NotificationController', () => { const module: TestingModule = await Test.createTestingModule({ imports: [ ConfigModule.forRoot({ - envFilePath: ['.env.local', '.env'], + envFilePath: ['.env.test', '.env'], isGlobal: true, }), ], diff --git a/dictation_server/src/features/notification/test/notification.service.mock.ts b/dictation_server/src/features/notification/test/notification.service.mock.ts index b11c985..1984c27 100644 --- a/dictation_server/src/features/notification/test/notification.service.mock.ts +++ b/dictation_server/src/features/notification/test/notification.service.mock.ts @@ -21,7 +21,7 @@ export const makeNotificationServiceMock = async ( providers: [NotificationService], imports: [ ConfigModule.forRoot({ - envFilePath: ['.env.local', '.env'], + envFilePath: ['.env.test', '.env'], }), ], }) diff --git a/dictation_server/src/features/tasks/tasks.controller.spec.ts b/dictation_server/src/features/tasks/tasks.controller.spec.ts index 01e7e75..72b8ac5 100644 --- a/dictation_server/src/features/tasks/tasks.controller.spec.ts +++ b/dictation_server/src/features/tasks/tasks.controller.spec.ts @@ -10,7 +10,7 @@ describe('TasksController', () => { const module: TestingModule = await Test.createTestingModule({ imports: [ ConfigModule.forRoot({ - envFilePath: ['.env.local', '.env'], + envFilePath: ['.env.test', '.env'], isGlobal: true, }), ], diff --git a/dictation_server/src/features/tasks/tasks.service.spec.ts b/dictation_server/src/features/tasks/tasks.service.spec.ts index ad85be7..f0de160 100644 --- a/dictation_server/src/features/tasks/tasks.service.spec.ts +++ b/dictation_server/src/features/tasks/tasks.service.spec.ts @@ -41,6 +41,7 @@ import { Roles } from '../../common/types/role'; import { TasksRepositoryService } from '../../repositories/tasks/tasks.repository.service'; import { overrideBlobstorageService } from '../../common/test/overrides'; import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service'; +import { truncateAllTable } from '../../common/test/init'; describe('TasksService', () => { it('タスク一覧を取得できる(admin)', async () => { @@ -588,20 +589,32 @@ describe('TasksService', () => { describe('DBテスト', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -779,20 +792,32 @@ describe('TasksService', () => { describe('changeCheckoutPermission', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -1409,20 +1434,32 @@ describe('changeCheckoutPermission', () => { describe('checkout', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -1609,7 +1646,6 @@ describe('checkout', () => { account_id: accountId, external_id: 'typist-user-external-id', role: 'typist', - author_id: 'MY_AUTHOR_ID', }); const { id: authorUserId } = await makeTestUser(source, { account_id: accountId, @@ -1656,7 +1692,6 @@ describe('checkout', () => { account_id: accountId, external_id: 'typist-user-external-id', role: 'typist', - author_id: 'MY_AUTHOR_ID', }); const { id: authorUserId } = await makeTestUser(source, { account_id: accountId, @@ -1993,20 +2028,32 @@ describe('checkout', () => { describe('checkin', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -2196,20 +2243,32 @@ describe('checkin', () => { describe('suspend', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -2395,20 +2454,32 @@ describe('suspend', () => { describe('cancel', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -3011,20 +3082,32 @@ describe('cancel', () => { describe('backup', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -3305,20 +3388,32 @@ describe('backup', () => { describe('getNextTask', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); diff --git a/dictation_server/src/features/tasks/test/utility.ts b/dictation_server/src/features/tasks/test/utility.ts index c691934..1d8efc9 100644 --- a/dictation_server/src/features/tasks/test/utility.ts +++ b/dictation_server/src/features/tasks/test/utility.ts @@ -48,7 +48,7 @@ export const makeTaskTestingModuleWithNotificaiton = async ( const module: TestingModule = await Test.createTestingModule({ imports: [ ConfigModule.forRoot({ - envFilePath: ['.env.local', '.env'], + envFilePath: ['.env.test', '.env'], isGlobal: true, }), AuthModule, diff --git a/dictation_server/src/features/templates/templates.controller.spec.ts b/dictation_server/src/features/templates/templates.controller.spec.ts index 00bd2fc..20ffb00 100644 --- a/dictation_server/src/features/templates/templates.controller.spec.ts +++ b/dictation_server/src/features/templates/templates.controller.spec.ts @@ -10,7 +10,7 @@ describe('TemplatesController', () => { const module: TestingModule = await Test.createTestingModule({ imports: [ ConfigModule.forRoot({ - envFilePath: ['.env.local', '.env'], + envFilePath: ['.env.test', '.env'], isGlobal: true, }), ], diff --git a/dictation_server/src/features/templates/templates.service.spec.ts b/dictation_server/src/features/templates/templates.service.spec.ts index a366b30..8ef101f 100644 --- a/dictation_server/src/features/templates/templates.service.spec.ts +++ b/dictation_server/src/features/templates/templates.service.spec.ts @@ -7,24 +7,37 @@ import { makeContext } from '../../common/log'; import { TemplateFilesRepositoryService } from '../../repositories/template_files/template_files.repository.service'; import { HttpException, HttpStatus } from '@nestjs/common'; import { makeErrorResponse } from '../../common/error/makeErrorResponse'; +import { truncateAllTable } from '../../common/test/init'; describe('getTemplates', () => { - let source: DataSource | undefined = undefined; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + let source: DataSource | null = null; + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); - source = undefined; + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); + source = null; }); it('テンプレートファイル一覧を取得できる', async () => { diff --git a/dictation_server/src/features/terms/terms.service.spec.ts b/dictation_server/src/features/terms/terms.service.spec.ts index 5950404..4a6ac7e 100644 --- a/dictation_server/src/features/terms/terms.service.spec.ts +++ b/dictation_server/src/features/terms/terms.service.spec.ts @@ -6,23 +6,36 @@ import { makeContext } from '../../common/log'; import { v4 as uuidv4 } from 'uuid'; import { HttpException, HttpStatus } from '@nestjs/common'; import { makeErrorResponse } from '../../common/error/makeErrorResponse'; +import { truncateAllTable } from '../../common/test/init'; describe('利用規約取得', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); diff --git a/dictation_server/src/features/users/test/users.service.mock.ts b/dictation_server/src/features/users/test/users.service.mock.ts index 0984d0f..c2da3db 100644 --- a/dictation_server/src/features/users/test/users.service.mock.ts +++ b/dictation_server/src/features/users/test/users.service.mock.ts @@ -67,7 +67,7 @@ export const makeUsersServiceMock = async ( providers: [UsersService], imports: [ ConfigModule.forRoot({ - envFilePath: ['.env.local', '.env'], + envFilePath: ['.env.test', '.env'], }), ], }) diff --git a/dictation_server/src/features/users/test/utility.ts b/dictation_server/src/features/users/test/utility.ts index 58a7512..849aa34 100644 --- a/dictation_server/src/features/users/test/utility.ts +++ b/dictation_server/src/features/users/test/utility.ts @@ -113,7 +113,7 @@ export const makeTestingModuleWithAdb2c = async ( const module: TestingModule = await Test.createTestingModule({ imports: [ ConfigModule.forRoot({ - envFilePath: ['.env.local', '.env'], + envFilePath: ['.env.test', '.env'], isGlobal: true, }), AuthModule, diff --git a/dictation_server/src/features/users/users.controller.spec.ts b/dictation_server/src/features/users/users.controller.spec.ts index b64a9fc..d060ac2 100644 --- a/dictation_server/src/features/users/users.controller.spec.ts +++ b/dictation_server/src/features/users/users.controller.spec.ts @@ -13,7 +13,7 @@ describe('UsersController', () => { const module: TestingModule = await Test.createTestingModule({ imports: [ ConfigModule.forRoot({ - envFilePath: ['.env.local', '.env'], + envFilePath: ['.env.test', '.env'], isGlobal: true, }), ], diff --git a/dictation_server/src/features/users/users.service.spec.ts b/dictation_server/src/features/users/users.service.spec.ts index 82a8199..dc3b656 100644 --- a/dictation_server/src/features/users/users.service.spec.ts +++ b/dictation_server/src/features/users/users.service.spec.ts @@ -1,5 +1,4 @@ import { HttpException, HttpStatus } from '@nestjs/common'; -import { AccessToken } from '../../common/token'; import { makeErrorResponse } from '../../common/error/makeErrorResponse'; import { makeDefaultAdB2cMockValue, @@ -47,21 +46,36 @@ import { import { v4 as uuidv4 } from 'uuid'; import { createOptionItems, createWorktype } from '../accounts/test/utility'; import { createWorkflow, getWorkflows } from '../workflows/test/utility'; +import { truncateAllTable } from '../../common/test/init'; describe('UsersService.confirmUser', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -420,20 +434,32 @@ describe('UsersService.confirmUserAndInitPassword', () => { describe('UsersService.createUser', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -1365,20 +1391,32 @@ describe('UsersService.createUser', () => { describe('UsersService.getUsers', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -1389,6 +1427,16 @@ describe('UsersService.getUsers', () => { if (!module) fail(); const { id: accountId } = await makeTestSimpleAccount(source); + const { id: typistUserId } = await makeTestUser(source, { + account_id: accountId, + external_id: 'external_id2', + role: 'typist', + author_id: undefined, + auto_renew: true, + encryption: false, + encryption_password: undefined, + prompt: false, + }); const { external_id: externalId_author, id: authorUserId } = await makeTestUser(source, { account_id: accountId, @@ -1401,17 +1449,6 @@ describe('UsersService.getUsers', () => { prompt: false, }); - const { id: typistUserId } = await makeTestUser(source, { - account_id: accountId, - external_id: 'external_id2', - role: 'typist', - author_id: undefined, - auto_renew: true, - encryption: false, - encryption_password: undefined, - prompt: false, - }); - await createUserGroup(source, accountId, 'group1', [typistUserId]); const { id: noneUserId } = await makeTestUser(source, { @@ -1843,20 +1880,32 @@ describe('UsersService.getSortCriteria', () => { describe('UsersService.updateUser', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -2427,20 +2476,32 @@ describe('UsersService.updateUser', () => { describe('UsersService.updateAcceptedVersion', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -2515,20 +2576,32 @@ describe('UsersService.updateAcceptedVersion', () => { describe('UsersService.getUserName', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -2557,20 +2630,32 @@ describe('UsersService.getUserName', () => { describe('UsersService.getRelations', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -2622,6 +2707,8 @@ describe('UsersService.getRelations', () => { // 作成したデータを確認 { const workflows = await getWorkflows(source, account.id); + workflows.sort((a, b) => a.id - b.id); + expect(workflows.length).toBe(4); expect(workflows[0].worktype_id).toBe(worktype1.id); expect(workflows[0].author_id).toBe(user1); diff --git a/dictation_server/src/features/workflows/workflows.controller.spec.ts b/dictation_server/src/features/workflows/workflows.controller.spec.ts index afab754..3b12b75 100644 --- a/dictation_server/src/features/workflows/workflows.controller.spec.ts +++ b/dictation_server/src/features/workflows/workflows.controller.spec.ts @@ -10,7 +10,7 @@ describe('WorkflowsController', () => { const module: TestingModule = await Test.createTestingModule({ imports: [ ConfigModule.forRoot({ - envFilePath: ['.env.local', '.env'], + envFilePath: ['.env.test', '.env'], isGlobal: true, }), ], diff --git a/dictation_server/src/features/workflows/workflows.service.spec.ts b/dictation_server/src/features/workflows/workflows.service.spec.ts index 177a048..d04e4c5 100644 --- a/dictation_server/src/features/workflows/workflows.service.spec.ts +++ b/dictation_server/src/features/workflows/workflows.service.spec.ts @@ -19,23 +19,36 @@ import { overrideAdB2cService } from '../../common/test/overrides'; import { WorkflowsRepositoryService } from '../../repositories/workflows/workflows.repository.service'; import { HttpException, HttpStatus } from '@nestjs/common'; import { makeErrorResponse } from '../../common/error/makeErrorResponse'; +import { truncateAllTable } from '../../common/test/init'; describe('getWorkflows', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await(async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -47,22 +60,23 @@ describe('getWorkflows', () => { const { account, admin } = await makeTestAccount(source, { tier: 5 }); const { id: authorId1 } = await makeTestUser(source, { external_id: 'author1', - author_id: 'AUTHOR1', + author_id: 'BBBBB', account_id: account.id, role: USER_ROLES.AUTHOR, }); const { id: authorId2 } = await makeTestUser(source, { external_id: 'author2', - author_id: 'AUTHOR2', + author_id: 'AAAAA', account_id: account.id, role: USER_ROLES.AUTHOR, }); const { id: authorId3 } = await makeTestUser(source, { external_id: 'author3', - author_id: 'AUTHOR3', + author_id: 'CCCCC', account_id: account.id, role: USER_ROLES.AUTHOR, }); + const { id: typistId, external_id: typistExternalId } = await makeTestUser( source, { @@ -79,6 +93,12 @@ describe('getWorkflows', () => { ); const { id: worktypeId1 } = await createWorktype( + source, + account.id, + 'worktype2', + ); + + const { id: worktypeId2 } = await createWorktype( source, account.id, 'worktype1', @@ -95,7 +115,7 @@ describe('getWorkflows', () => { source, account.id, authorId1, - worktypeId1, + worktypeId2, templateId1, ); const workflow2 = await createWorkflow( @@ -112,10 +132,18 @@ describe('getWorkflows', () => { worktypeId1, undefined, ); + const workflow4 = await createWorkflow( + source, + account.id, + authorId3, + worktypeId2, + undefined, + ); await createWorkflowTypist(source, workflow1.id, typistId, undefined); await createWorkflowTypist(source, workflow2.id, undefined, userGroupId); await createWorkflowTypist(source, workflow3.id, undefined, userGroupId); + await createWorkflowTypist(source, workflow4.id, undefined, userGroupId); const service = module.get(WorkflowsService); const context = makeContext(admin.external_id, 'requestId'); @@ -123,10 +151,10 @@ describe('getWorkflows', () => { //作成したデータを確認 { const workflows = await getWorkflows(source, account.id); - expect(workflows.length).toBe(3); + expect(workflows.length).toBe(4); expect(workflows[0].id).toBe(workflow1.id); expect(workflows[0].author_id).toBe(authorId1); - expect(workflows[0].worktype_id).toBe(worktypeId1); + expect(workflows[0].worktype_id).toBe(worktypeId2); expect(workflows[0].template_id).toBe(templateId1); expect(workflows[1].id).toBe(workflow2.id); @@ -138,6 +166,11 @@ describe('getWorkflows', () => { expect(workflows[2].author_id).toBe(authorId3); expect(workflows[2].worktype_id).toBe(worktypeId1); expect(workflows[2].template_id).toBe(null); + + expect(workflows[3].id).toBe(workflow4.id); + expect(workflows[3].author_id).toBe(authorId3); + expect(workflows[3].worktype_id).toBe(worktypeId2); + expect(workflows[3].template_id).toBe(null); } overrideAdB2cService(service, { @@ -148,37 +181,48 @@ describe('getWorkflows', () => { //実行結果を確認 { - expect(resWorkflows.length).toBe(3); - expect(resWorkflows[0].id).toBe(workflow1.id); - expect(resWorkflows[0].author.id).toBe(authorId1); - expect(resWorkflows[0].author.authorId).toBe('AUTHOR1'); - expect(resWorkflows[0].worktype?.id).toBe(worktypeId1); - expect(resWorkflows[0].worktype?.worktypeId).toBe('worktype1'); + expect(resWorkflows.length).toBe(4); + + expect(resWorkflows[0].id).toBe(workflow2.id); + expect(resWorkflows[0].author.id).toBe(authorId2); + expect(resWorkflows[0].author.authorId).toBe('AAAAA'); + expect(resWorkflows[0].worktype).toBe(undefined); expect(resWorkflows[0].template?.id).toBe(templateId1); expect(resWorkflows[0].template?.fileName).toBe('fileName1'); expect(resWorkflows[0].typists.length).toBe(1); - expect(resWorkflows[0].typists[0].typistUserId).toBe(typistId); - expect(resWorkflows[0].typists[0].typistName).toBe('typist1'); + expect(resWorkflows[0].typists[0].typistGroupId).toBe(userGroupId); + expect(resWorkflows[0].typists[0].typistName).toBe('group1'); - expect(resWorkflows[1].id).toBe(workflow2.id); - expect(resWorkflows[1].author.id).toBe(authorId2); - expect(resWorkflows[1].author.authorId).toBe('AUTHOR2'); - expect(resWorkflows[1].worktype).toBe(undefined); + expect(resWorkflows[1].id).toBe(workflow1.id); + expect(resWorkflows[1].author.id).toBe(authorId1); + expect(resWorkflows[1].author.authorId).toBe('BBBBB'); + expect(resWorkflows[1].worktype?.id).toBe(worktypeId2); + expect(resWorkflows[1].worktype?.worktypeId).toBe('worktype1'); expect(resWorkflows[1].template?.id).toBe(templateId1); expect(resWorkflows[1].template?.fileName).toBe('fileName1'); expect(resWorkflows[1].typists.length).toBe(1); - expect(resWorkflows[1].typists[0].typistGroupId).toBe(userGroupId); - expect(resWorkflows[1].typists[0].typistName).toBe('group1'); + expect(resWorkflows[1].typists[0].typistUserId).toBe(typistId); + expect(resWorkflows[1].typists[0].typistName).toBe('typist1'); - expect(resWorkflows[2].id).toBe(workflow3.id); + expect(resWorkflows[2].id).toBe(workflow4.id); expect(resWorkflows[2].author.id).toBe(authorId3); - expect(resWorkflows[2].author.authorId).toBe('AUTHOR3'); - expect(resWorkflows[2].worktype?.id).toBe(worktypeId1); + expect(resWorkflows[2].author.authorId).toBe('CCCCC'); + expect(resWorkflows[2].worktype?.id).toBe(worktypeId2); expect(resWorkflows[2].worktype?.worktypeId).toBe('worktype1'); expect(resWorkflows[2].template).toBe(undefined); expect(resWorkflows[2].typists.length).toBe(1); expect(resWorkflows[2].typists[0].typistGroupId).toBe(userGroupId); expect(resWorkflows[2].typists[0].typistName).toBe('group1'); + + expect(resWorkflows[3].id).toBe(workflow3.id); + expect(resWorkflows[3].author.id).toBe(authorId3); + expect(resWorkflows[3].author.authorId).toBe('CCCCC'); + expect(resWorkflows[3].worktype?.id).toBe(worktypeId1); + expect(resWorkflows[3].worktype?.worktypeId).toBe('worktype2'); + expect(resWorkflows[3].template).toBe(undefined); + expect(resWorkflows[3].typists.length).toBe(1); + expect(resWorkflows[3].typists[0].typistGroupId).toBe(userGroupId); + expect(resWorkflows[3].typists[0].typistName).toBe('group1'); } }); @@ -209,7 +253,7 @@ describe('getWorkflows', () => { const module = await makeTestingModule(source); if (!module) fail(); // 第五階層のアカウント作成 - const { account, admin } = await makeTestAccount(source, { tier: 5 }); + const { admin } = await makeTestAccount(source, { tier: 5 }); const service = module.get(WorkflowsService); const context = makeContext(admin.external_id, 'requestId'); @@ -236,22 +280,34 @@ describe('getWorkflows', () => { describe('createWorkflows', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); - }); - afterEach(async () => { - if (!source) return; - await source.destroy(); - source = null; + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); + source = null; + }); it('アカウント内にWorkflowを作成できる(WorktypeIDあり、テンプレートファイルあり)', async () => { if (!source) fail(); const module = await makeTestingModule(source); @@ -575,6 +631,7 @@ describe('createWorkflows', () => { //実行結果を確認 { const workflows = await getWorkflows(source, account.id); + workflows.sort((a, b) => a.id - b.id); expect(workflows.length).toBe(2); expect(workflows[1].account_id).toBe(account.id); expect(workflows[1].author_id).toBe(authorId); @@ -1162,19 +1219,32 @@ describe('createWorkflows', () => { describe('updateWorkflow', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); @@ -1592,6 +1662,7 @@ describe('updateWorkflow', () => { //作成したデータを確認 { const workflows = await getWorkflows(source, account.id); + workflows.sort((a, b) => a.id - b.id); const workflowTypists = await getAllWorkflowTypists(source); expect(workflows.length).toBe(2); expect(workflows[0].id).toBe(preWorkflow1.id); @@ -1627,6 +1698,7 @@ describe('updateWorkflow', () => { //実行結果を確認 { const workflows = await getWorkflows(source, account.id); + workflows.sort((a, b) => a.id - b.id); expect(workflows.length).toBe(2); expect(workflows[1].account_id).toBe(account.id); expect(workflows[1].author_id).toBe(authorId2); @@ -2349,19 +2421,32 @@ describe('updateWorkflow', () => { describe('deleteWorkflows', () => { let source: DataSource | null = null; - beforeEach(async () => { - source = new DataSource({ - type: 'sqlite', - database: ':memory:', - logging: false, - entities: [__dirname + '/../../**/*.entity{.ts,.js}'], - synchronize: true, // trueにすると自動的にmigrationが行われるため注意 - }); - return source.initialize(); + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + }); + return await s.initialize(); + })(); + } }); - afterEach(async () => { - if (!source) return; - await source.destroy(); + + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); source = null; }); diff --git a/dictation_server/src/repositories/users/users.repository.service.ts b/dictation_server/src/repositories/users/users.repository.service.ts index 9237bdd..ae3fccc 100644 --- a/dictation_server/src/repositories/users/users.repository.service.ts +++ b/dictation_server/src/repositories/users/users.repository.service.ts @@ -29,6 +29,7 @@ import { TIERS, TRIAL_LICENSE_ISSUE_NUM, USER_ROLES, + USER_ROLE_ORDERS, } from '../../constants'; import { License } from '../licenses/entity/license.entity'; import { NewTrialLicenseExpirationDate } from '../../features/licenses/types/types'; @@ -496,10 +497,23 @@ export class UsersRepositoryService { license: true, }, where: { account_id: accountId }, + comment: `${context.getTrackingId()}_${new Date().toUTCString()}`, }); - return dbUsers; + // RoleのAuthor、Typist、Noneの順に並び替える + const roleSortedUsers = dbUsers.sort((a, b) => { + // Roleが同じ場合はIDの昇順で並び替える + if (a.role === b.role) { + return a.id - b.id; + } + + return ( + USER_ROLE_ORDERS.indexOf(a.role) - USER_ROLE_ORDERS.indexOf(b.role) + ); + }); + + return roleSortedUsers; }); } diff --git a/dictation_server/src/repositories/workflows/workflows.repository.service.ts b/dictation_server/src/repositories/workflows/workflows.repository.service.ts index b945380..cf28006 100644 --- a/dictation_server/src/repositories/workflows/workflows.repository.service.ts +++ b/dictation_server/src/repositories/workflows/workflows.repository.service.ts @@ -50,7 +50,12 @@ export class WorkflowsRepositoryService { }, }, order: { - id: 'ASC', + author: { + author_id: 'ASC', + }, + worktype: { + custom_worktype_id: 'ASC', + }, }, comment: `${context.getTrackingId()}_${new Date().toUTCString()}`, });