diff --git a/pkg/accounts/service/editAccount.test.ts b/pkg/accounts/service/editAccount.test.ts index b6e1848d..78b3199a 100644 --- a/pkg/accounts/service/editAccount.test.ts +++ b/pkg/accounts/service/editAccount.test.ts @@ -1,5 +1,5 @@ import { Option, Result } from '@mikuroxina/mini-fn'; -import { afterEach, describe, expect, it } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; import type { ID } from '../../id/type.js'; import { Argon2idPasswordEncoder } from '../../password/mod.js'; @@ -8,313 +8,283 @@ import { Account, type AccountID } from '../model/account.js'; import { EditAccountService } from './editAccount.js'; import { EtagVerifyService } from './etagGenerateVerify.js'; +const passwordEncoder = new Argon2idPasswordEncoder(); const repository = new InMemoryAccountRepository(); -await repository.create( - Account.new({ - id: '1' as ID, - name: '@john@example.com', - mail: 'johndoe@example.com', - nickname: 'John Doe', - passphraseHash: 'hash', - bio: '', - role: 'normal', - frozen: 'normal', - silenced: 'normal', - status: 'notActivated', - createdAt: new Date(), - }), -); const etagVerifyService = new EtagVerifyService(); const editAccountService = new EditAccountService( repository, etagVerifyService, - new Argon2idPasswordEncoder(), + passwordEncoder, ); describe('EditAccountService', () => { - afterEach(() => repository.reset()); - - it('should be success to update nickname', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - - const etag = await etagVerifyService.generate(account[1]); - const updateRes = await editAccountService.editNickname( - etag, - '@john@example.com', - 'new nickname', + let account: Account; + let etag: string; + + beforeEach(async () => { + await repository.create( + Account.new({ + id: '1' as ID, + name: '@john@example.com', + mail: 'johndoe@example.com', + nickname: 'John Doe', + passphraseHash: 'hash', + bio: '', + role: 'normal', + frozen: 'normal', + silenced: 'normal', + status: 'notActivated', + createdAt: new Date(), + }), ); - expect(Result.isErr(updateRes)).toBe(false); - expect(updateRes[1]).toBe(true); - expect(account[1].getNickname()).toBe('new nickname'); - }); + const res = await repository.findByName('@john@example.com'); + if (Option.isNone(res)) return; - it('should be fail to update nickname when nickname shorter more than 1', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - - const etag = await etagVerifyService.generate(account[1]); - const updateRes = await editAccountService.editNickname( - etag, - '@john@example.com', - '', - ); - expect(Result.isErr(updateRes)).toBe(true); + etag = await etagVerifyService.generate(res[1]); + account = res[1]; }); + afterEach(() => repository.reset()); - it('should be fail to update nickname when nickname more than 256', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - - const etag = await etagVerifyService.generate(account[1]); - const updateRes = await editAccountService.editNickname( - etag, - '@john@example.com', - 'a'.repeat(257), - ); - expect(Result.isErr(updateRes)).toBe(true); - }); - - it('should be success to update nickname when nickname 256', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - - const etag = await etagVerifyService.generate(account[1]); - const updateRes = await editAccountService.editNickname( - etag, - '@john@example.com', - 'a'.repeat(256), - ); - expect(Result.isErr(updateRes)).toBe(false); - expect(updateRes[1]).toBe(true); - expect(account[1].getNickname()).toBe('a'.repeat(256)); - }); - - it('should be success to update nickname when nickname 1', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - - const etag = await etagVerifyService.generate(account[1]); - const updateRes = await editAccountService.editNickname( - etag, - '@john@example.com', - 'a', - ); - expect(Result.isErr(updateRes)).toBe(false); - expect(updateRes[1]).toBe(true); - }); - - it('should be fail to update nickname when etag not match', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - - const etag = await etagVerifyService.generate(account[1]); - const res = await editAccountService.editNickname( - etag, - '@john@example.com', - 'new nickname', - ); - expect(Result.isErr(res)).toBe(true); - }); - - it('should be fail to update nickname when account not found', async () => { - const res = await editAccountService.editNickname( - 'invalid etag', - '@foo@example.com', - 'new nickname', - ); - expect(Result.isErr(res)).toBe(true); - }); - - it('should be success to update passphrase', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - - const etag = await etagVerifyService.generate(account[1]); - const updateRes = await editAccountService.editNickname( - etag, - '@john@example.com', - 'new password', - ); - expect(Result.isErr(updateRes)).toBe(false); - expect(updateRes[1]).toBe(true); - }); - - it('should be fail to update passphrase when passphrase shorter more than 8', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - - const etag = await etagVerifyService.generate(account[1]); - const updateRes = await editAccountService.editNickname( - etag, - '@john@example.com', - 'a'.repeat(7), - ); - expect(Result.isErr(updateRes)).toBe(true); - }); - - it('should be fail to update passphrase when passphrase longer more than 512', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - - const etag = await etagVerifyService.generate(account[1]); - const updateRes = await editAccountService.editNickname( - etag, - '@john@example.com', - 'a'.repeat(513), - ); - expect(Result.isErr(updateRes)).toBe(true); - }); - - it('should be success to update passphrase when passphrase 8', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - - const etag = await etagVerifyService.generate(account[1]); - const updateRes = await editAccountService.editNickname( - etag, - '@john@example.com', - 'a'.repeat(8), - ); - expect(Result.isErr(updateRes)).toBe(false); - expect(updateRes[1]).toBe(true); - }); - - it('should be success to update passphrase when passphrase 512', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - - const etag = await etagVerifyService.generate(account[1]); - const updateRes = await editAccountService.editNickname( - etag, - '@john@example.com', - 'a'.repeat(512), - ); - expect(Result.isErr(updateRes)).toBe(false); - expect(updateRes[1]).toBe(true); - }); - - it('should be fail to update passphrase when etag not match', async () => { - const res = await editAccountService.editPassphrase( - 'invalid_etag', - '@john@example.com', - 'new password', - ); - expect(Result.isErr(res)).toBe(true); - }); - - it('should be fail to update passphrase when account not found', async () => { - const res = await editAccountService.editPassphrase( - 'invalid etag', - '@john@example.com', - 'new password', - ); - expect(Result.isErr(res)).toBe(true); - }); - - it('should be success to update email', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - - const etag = await etagVerifyService.generate(account[1]); - const updateRes = await editAccountService.editEmail( - etag, - '@john@example.com', - 'pulsate@example.com', + describe('nickname', () => { + it.each([ + { + title: 'nickname', + nickname: 'new nickname', + }, + { + title: 'when nickname length 256', + nickname: 'a'.repeat(256), + }, + { + title: 'when nickname length 1', + nickname: 'a', + }, + ])( + 'should be success to update $title', // + async ({ nickname }) => { + const updateRes = await editAccountService.editNickname( + etag, + '@john@example.com', + nickname, + ); + expect(Result.isOk(updateRes)).toBe(true); + + expect(account.getNickname()).toBe(nickname); + expect((await repository.findByName('@john@example.com'))[1]).toBe( + account, + ); + }, ); - expect(Result.isErr(updateRes)).toBe(false); - expect(updateRes[1]).toBe(true); - }); - it('should be fail to update email when etag not match', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - - const updateRes = await editAccountService.editEmail( - 'invalid_etag', - '@john@example.com', - 'pulsate@example.com', + it.each([ + { + title: 'nickname length shorter more than 1', + nickname: '', + }, + { + title: 'nickname length more than 256', + nickname: 'a'.repeat(257), + }, + { + title: 'etag not match', + etag: 'invalid', + nickname: 'new nickname', + }, + { + title: 'account not found', + name: '@foo@example.com' as const, + nickname: 'new nickname', + }, + ])( + 'should be fail to update nickname when $title', + async ({ etag: invalid, name, nickname }) => { + const updateRes = await editAccountService.editNickname( + invalid ?? etag, + name ?? '@john@example.com', + nickname, + ); + expect(Result.isErr(updateRes)).toBe(true); + }, ); - expect(Result.isErr(updateRes)).toBe(true); }); - it('should be fail to update email when account not found', async () => { - const updateRes = await editAccountService.editEmail( - 'invalid etag', - '@john@example.com', - 'pulsate@pulsate.mail', + describe('passphrase', () => { + it.each([ + { + title: 'passphrase', + passphrase: 'new password', + }, + { + title: 'when passphrase length 8', + passphrase: 'a'.repeat(8), + }, + { + title: 'when passphrase length 512', + passphrase: 'a'.repeat(512), + }, + ])( + 'should be success to update $title', // + async ({ passphrase }) => { + const updateRes = await editAccountService.editPassphrase( + etag, + '@john@example.com', + passphrase, + ); + expect(Result.isOk(updateRes)).toBe(true); + + expect( + await passwordEncoder.isMatchPassword( + passphrase, + account.getPassphraseHash() ?? '', + ), + ).toBe(true); + expect((await repository.findByName('@john@example.com'))[1]).toBe( + account, + ); + }, ); - expect(Result.isErr(updateRes)).toBe(true); - }); - it('should be success to update email when email shortest', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - - const etag = await etagVerifyService.generate(account[1]); - const updateRes = await editAccountService.editEmail( - etag, - '@john@example.com', - 'a'.repeat(7), + it.each([ + { + title: 'shorter more than 8', + passphrase: 'a'.repeat(7), + }, + { + title: 'longer more than 512', + passphrase: 'a'.repeat(513), + }, + { + title: 'etag not match', + etag: 'invalid', + passphrase: 'new password', + }, + { + title: 'account not found', + name: '@foo@example.com' as const, + passphrase: 'new password', + }, + ])( + 'should be failed to update passphrase when $title', + async ({ etag: invalid, name, passphrase }) => { + const updateRes = await editAccountService.editPassphrase( + invalid ?? etag, + name ?? '@john@example.com', + passphrase, + ); + + expect(Result.isErr(updateRes)).toBe(true); + }, ); - expect(Result.isErr(updateRes)).toBe(false); - expect(updateRes[1]).toBe(true); }); - it('should be success to update email when email length 8', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - - const etag = await etagVerifyService.generate(account[1]); - const updateRes = await editAccountService.editEmail( - etag, - '@john@example.com', - 'a'.repeat(8), + describe('email', () => { + it.each([ + { + title: 'email', + email: 'pulsate@example.com', + }, + { + title: 'when shortest', + email: 'a'.repeat(7), + }, + { + title: 'when email length 8', + email: 'a'.repeat(8), + }, + { + title: 'when email length 319', + email: 'a'.repeat(319), + }, + ])( + 'should be success to update $title', // + async ({ email }) => { + const updateRes = await editAccountService.editEmail( + etag, + '@john@example.com', + email, + ); + expect(Result.isOk(updateRes)).toBe(true); + + expect(account.getMail()).toBe(email); + expect((await repository.findByName('@john@example.com'))[1]).toBe( + account, + ); + }, ); - expect(Result.isErr(updateRes)).toBe(false); - expect(updateRes[1]).toBe(true); - }); - - it('should be fail to update email when email too long', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - const etag = await etagVerifyService.generate(account[1]); - const updateRes = await editAccountService.editEmail( - etag, - '@john@example.com', - 'a'.repeat(320), + it.each([ + { + title: 'etag not match', + etag: 'invalid', + email: 'pulsate@example.com', + }, + { + title: 'account not found', + name: '@foo@example.com' as const, + email: 'pulsate@example.com', + }, + { + title: 'too long', + email: 'a'.repeat(320), + }, + ])( + 'should be fail to update email when $title', + async ({ etag: invalid, name, email }) => { + const updateRes = await editAccountService.editEmail( + invalid ?? etag, + name ?? '@john@example.com', + email, + ); + expect(Result.isErr(updateRes)).toBe(true); + }, ); - expect(Result.isErr(updateRes)).toBe(true); }); - it('should be success to update email when email length 319', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - - const etag = await etagVerifyService.generate(account[1]); - const updateRes = await editAccountService.editEmail( - etag, - '@john@example.com', - 'a'.repeat(319), + describe('bio', () => { + it.each([ + { + bio: 'new bio', + }, + ])( + 'should be success to update bio', // + async ({ bio }) => { + const updateRes = await editAccountService.editBio( + etag, + '@john@example.com', + bio, + ); + + expect(Result.isOk(updateRes)).toBe(true); + + expect(account.getBio()).toBe(bio); + expect((await repository.findByName('@john@example.com'))[1]).toBe( + account, + ); + }, ); - expect(Result.isErr(updateRes)).toBe(false); - expect(updateRes[1]).toBe(true); - }); - it('should be success to update bio', async () => { - const account = await repository.findByName('@john@example.com'); - if (Option.isNone(account)) return; - - const etag = await etagVerifyService.generate(account[1]); - const updateRes = await editAccountService.editBio( - etag, - '@john@example.com', - 'new bio', + it.each([ + { + title: 'etag not match', + etag: 'invalid', + bio: 'new bio', + }, + { + title: 'account not found', + name: '@foo@example.com' as const, + bio: 'new bio', + }, + ])( + 'should be fail to update bio when $title', + async ({ etag: invalid, name, bio }) => { + const updateRes = await editAccountService.editBio( + invalid ?? etag, + name ?? '@john@example.com', + bio, + ); + + expect(Result.isErr(updateRes)).toBe(true); + }, ); - - expect(Result.isErr(updateRes)).toBe(false); - expect(updateRes[1]).toBe(true); - expect(account[1].getBio).toBe('new bio'); }); }); diff --git a/pkg/accounts/service/editAccount.ts b/pkg/accounts/service/editAccount.ts index 575de7f8..83230ad0 100644 --- a/pkg/accounts/service/editAccount.ts +++ b/pkg/accounts/service/editAccount.ts @@ -53,6 +53,11 @@ export class EditAccountService { try { account.setNickName(nickname); + const res = await this.accountRepository.edit(account); + if (Result.isErr(res)) { + return res; + } + return Result.ok(true); } catch (e) { return Result.err(e as unknown as Error); @@ -86,6 +91,12 @@ export class EditAccountService { account.setPassphraseHash( await this.passwordEncoder.encodePassword(newPassphrase), ); + + const res = await this.accountRepository.edit(account); + if (Result.isErr(res)) { + return res; + } + return Result.ok(true); } catch (e) { return Result.err(e as unknown as Error); @@ -119,6 +130,12 @@ export class EditAccountService { try { account.setMail(newEmail); + + const res = await this.accountRepository.edit(account); + if (Result.isErr(res)) { + return res; + } + return Result.ok(true); } catch (e) { return Result.err(e as unknown as Error); @@ -141,6 +158,12 @@ export class EditAccountService { try { account.setBio(bio); + + const res = await this.accountRepository.edit(account); + if (Result.isErr(res)) { + return res; + } + return Result.ok(true); } catch (e) { return Result.err(e as unknown as Error);