• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

inclusion-numerique / coop-mediation-numerique / a499b582-7152-4ae6-bfae-41b25ff83c10

31 Mar 2026 03:49PM UTC coverage: 7.472% (-3.2%) from 10.692%
a499b582-7152-4ae6-bfae-41b25ff83c10

Pull #466

circleci

marc-gavanier
refactor(inscription): extract ajouterLieuxActivite into modular use-case

Refactor 174 lines of mixed logic into composable, testable functions:

- domain/types.ts: Pure type definitions
- domain/classifyLieu.ts: Pure classification logic (4 cases)
- domain/filterAlreadyLinked.ts: Pure filtering logic
- dependencies/types.ts: Injectable dependency contracts
- dependencies/prisma.ts: Production implementations
- dependencies/inMemory.ts: Test implementations (no mocks)
- strategies/processClassifiedLieu.ts: Case execution
- ajouterLieuxActivite.ts: Orchestration (~40 lines)

23 unit tests with real in-memory implementations.
Fixes issue where structures from API Entreprise were not being created.
Pull Request #466: refactor(inscription): extract ajouterLieuxActivite into modular use-case

500 of 10544 branches covered (4.74%)

Branch coverage included in aggregate %.

145 of 186 new or added lines in 15 files covered. (77.96%)

1379 existing lines in 152 files now uncovered.

1500 of 16224 relevant lines covered (9.25%)

36.99 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/apps/web/src/api-client/apiClient.ts
1
import { prismaClient } from '@app/web/prismaClient'
2
import { generateRandomSecret } from '@app/web/security/generateRandomSecret'
3
import { hashSecret, verifySecret } from '@app/web/security/hashSecret'
4
import { ApiClient, ApiClientScope } from '@prisma/client'
5

6
export type CreateApiClientInput = {
7
  id?: string
8
  name: string
9
  scopes: ApiClientScope[]
10
  validUntil?: Date
11
  validFrom?: Date
12
}
13

UNCOV
14
export const createApiClient = async ({
×
15
  id,
16
  name,
17
  scopes,
18
  validFrom,
19
  validUntil,
20
}: CreateApiClientInput): Promise<
21
  ApiClient & {
22
    secret: string
23
  }
24
> => {
UNCOV
25
  const secret = generateRandomSecret()
×
UNCOV
26
  const secretHash = hashSecret(secret)
×
27

UNCOV
28
  const apiClient = await prismaClient.apiClient.create({
×
29
    data: {
30
      id,
31
      name,
32
      secretHash, // Store the hashed secret in the database
33
      validFrom: validFrom ?? new Date(),
×
34
      validUntil,
35
      scopes,
36
    },
37
  })
38

UNCOV
39
  return {
×
40
    ...apiClient,
41
    secret,
42
    secretHash,
43
  }
44
}
45

46
export type CreateApiClientOutput = Awaited<ReturnType<typeof createApiClient>>
47

UNCOV
48
export const rotateApiClientSecret = async ({
×
49
  clientId,
50
}: {
51
  clientId: string
52
}) => {
UNCOV
53
  const secret = generateRandomSecret()
×
UNCOV
54
  const secretHash = hashSecret(secret)
×
55

UNCOV
56
  await prismaClient.apiClient.update({
×
57
    where: { id: clientId },
58
    data: {
59
      secretHash, // Store the hashed secret in the database
60
      updated: new Date(),
61
    },
62
  })
63

UNCOV
64
  return {
×
65
    clientId,
66
    secret,
67
    secretHash,
68
  }
69
}
70

UNCOV
71
export const changeApiClientScopes = async ({
×
72
  clientId,
73
  scopes,
74
}: {
75
  clientId: string
76
  scopes: ApiClientScope[]
77
}) => {
78
  const apiClient = await prismaClient.apiClient.findUnique({
×
79
    where: { id: clientId },
80
  })
81

82
  if (!apiClient) {
×
83
    throw new Error('Api client not found')
×
84
  }
85

86
  await prismaClient.apiClient.update({
×
87
    where: { id: clientId },
88
    data: {
89
      scopes,
90
      updated: new Date(),
91
    },
92
  })
93
}
94

UNCOV
95
export const authenticateApiCient = async (
×
96
  clientId: string,
97
  clientSecret: string,
98
): Promise<ApiClient | null> => {
99
  // Ensure that clientId is a uuid v4 using regex
UNCOV
100
  if (!/^[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12}$/.test(clientId)) return null
×
101

UNCOV
102
  const apiClient = await prismaClient.apiClient.findUnique({
×
103
    where: { id: clientId },
104
  })
105

UNCOV
106
  if (!apiClient) return null
×
107

UNCOV
108
  const isValid = verifySecret(clientSecret, apiClient.secretHash)
×
109

UNCOV
110
  if (!isValid) return null
×
111

UNCOV
112
  const now = new Date()
×
113

UNCOV
114
  if (apiClient.validFrom > now) return null
×
115

UNCOV
116
  if (!!apiClient.validUntil && apiClient.validUntil < now) {
×
UNCOV
117
    return null
×
118
  }
119

UNCOV
120
  return apiClient
×
121
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc