Skip to content

Commit 5566e15

Browse files
authored
MIM-530: add residence status (#292)
* add residence status * improve test * pr: Update src/services/property-base-service/tests/calculate-residence-status.test.ts
1 parent bfa4b90 commit 5566e15

File tree

5 files changed

+120
-5
lines changed

5 files changed

+120
-5
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Lease, LeaseStatus } from 'onecore-types'
2+
import { z } from 'zod'
3+
4+
import * as schemas from './schemas'
5+
6+
type ResidenceStatus = NonNullable<
7+
z.infer<typeof schemas.ResidenceDetailsSchema>['status']
8+
>
9+
10+
export function calculateResidenceStatus(leases: Lease[]): ResidenceStatus {
11+
const leased = leases.some((lease) =>
12+
[
13+
LeaseStatus.Current,
14+
LeaseStatus.Upcoming,
15+
LeaseStatus.AboutToEnd,
16+
].includes(lease.status)
17+
)
18+
19+
if (leased) {
20+
return 'LEASED'
21+
}
22+
23+
return 'VACANT'
24+
}

src/services/property-base-service/index.ts

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import KoaRouter from '@koa/router'
22

33
import * as propertyBaseAdapter from '../../adapters/property-base-adapter'
4+
import * as leasingAdapter from '../../adapters/leasing-adapter'
45

56
import { logger, generateRouteMetadata } from 'onecore-utilities'
67
import { registerSchema } from '../../utils/openapi'
78
import * as schemas from './schemas'
9+
import { calculateResidenceStatus } from './calculate-residence-status'
810

911
/**
1012
* @swagger
@@ -392,22 +394,51 @@ export const routes = (router: KoaRouter) => {
392394
const { residenceId } = ctx.params
393395

394396
try {
395-
const result = await propertyBaseAdapter.getResidenceDetails(residenceId)
396-
if (!result.ok) {
397-
if (result.err === 'not-found') {
397+
const getResidence =
398+
await propertyBaseAdapter.getResidenceDetails(residenceId)
399+
400+
if (!getResidence.ok) {
401+
if (getResidence.err === 'not-found') {
398402
ctx.status = 404
399403
ctx.body = { error: 'Residence not found', ...metadata }
400404
return
401405
}
402406

403-
logger.error(result.err, 'Internal server error', metadata)
407+
logger.error(getResidence.err, 'Internal server error', metadata)
404408
ctx.status = 500
405409
ctx.body = { error: 'Internal server error', ...metadata }
406410
return
407411
}
408412

413+
if (!getResidence.data.propertyObject.rentalId) {
414+
ctx.status = 200
415+
ctx.body = {
416+
content: schemas.ResidenceDetailsSchema.parse({
417+
...getResidence.data,
418+
status: null,
419+
}),
420+
...metadata,
421+
}
422+
return
423+
}
424+
425+
const leases = await leasingAdapter.getLeasesForPropertyId(
426+
getResidence.data.propertyObject.rentalId,
427+
{
428+
includeContacts: false,
429+
includeTerminatedLeases: false,
430+
includeUpcomingLeases: true,
431+
}
432+
)
433+
434+
const status = calculateResidenceStatus(leases)
435+
436+
ctx.status = 200
409437
ctx.body = {
410-
content: schemas.ResidenceDetailsSchema.parse(result.data),
438+
content: schemas.ResidenceDetailsSchema.parse({
439+
...getResidence.data,
440+
status,
441+
}),
411442
...metadata,
412443
}
413444
} catch (error) {

src/services/property-base-service/schemas.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ export const ResidenceDetailsSchema = z.object({
111111
id: z.string(),
112112
code: z.string(),
113113
name: z.string().nullable(),
114+
status: z.enum(['VACANT', 'LEASED']).nullable(),
114115
location: z.string().nullable(),
115116
accessibility: z.object({
116117
wheelchairAccessible: z.boolean(),
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { LeaseStatus } from 'onecore-types'
2+
3+
import { calculateResidenceStatus } from '../calculate-residence-status'
4+
import * as factory from '../../../../test/factories'
5+
6+
describe(calculateResidenceStatus, () => {
7+
it('returns "LEASED" when lease status is "Current", "Upcoming" or "AboutToEnd"', () => {
8+
expect(
9+
calculateResidenceStatus([
10+
factory.lease.build({ status: LeaseStatus.Current }),
11+
])
12+
).toBe('LEASED')
13+
14+
expect(
15+
calculateResidenceStatus([
16+
factory.lease.build({ status: LeaseStatus.Upcoming }),
17+
])
18+
).toBe('LEASED')
19+
20+
expect(
21+
calculateResidenceStatus([
22+
factory.lease.build({ status: LeaseStatus.AboutToEnd }),
23+
])
24+
).toBe('LEASED')
25+
})
26+
27+
it('returns "VACANT" when lease status is "Ended"', () => {
28+
const leases = [factory.lease.build({ status: LeaseStatus.Ended })]
29+
const status = calculateResidenceStatus(leases)
30+
expect(status).toBe('VACANT')
31+
})
32+
})

src/services/property-base-service/tests/index.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { z } from 'zod'
66

77
import { routes } from '../index'
88
import * as propertyBaseAdapter from '../../../adapters/property-base-adapter'
9+
import * as leasingAdapter from '../../../adapters/leasing-adapter'
910

1011
import * as factory from '../../../../test/factories'
1112
import {
@@ -15,6 +16,7 @@ import {
1516
RoomSchema,
1617
StaircaseSchema,
1718
} from '../schemas'
19+
import { LeaseStatus } from 'onecore-types'
1820

1921
const app = new Koa()
2022
const router = new KoaRouter()
@@ -144,6 +146,31 @@ describe('property-base-service', () => {
144146
expect(() => ResidenceSchema.parse(res.body.content)).not.toThrow()
145147
})
146148

149+
it('returns 200 and a residence with status', async () => {
150+
const residenceDetails = factory.residenceDetails.build({
151+
propertyObject: { rentalId: '1234' },
152+
})
153+
const getResidenceDetailsSpy = jest
154+
.spyOn(propertyBaseAdapter, 'getResidenceDetails')
155+
.mockResolvedValueOnce({ ok: true, data: residenceDetails })
156+
157+
const lease = factory.lease.build({ status: LeaseStatus.Current })
158+
const getLeasesSpy = jest
159+
.spyOn(leasingAdapter, 'getLeasesForPropertyId')
160+
.mockResolvedValueOnce([lease])
161+
162+
const res = await request(app.callback()).get(
163+
`/propertyBase/residence/${residenceDetails.id}`
164+
)
165+
166+
expect(res.status).toBe(200)
167+
expect(() => ResidenceSchema.parse(res.body.content)).not.toThrow()
168+
expect(res.body.content.status).toBe('LEASED')
169+
170+
expect(getResidenceDetailsSpy).toHaveBeenCalled()
171+
expect(getLeasesSpy).toHaveBeenCalled()
172+
})
173+
147174
it('returns 404 if no residence is found', async () => {
148175
const residence = factory.residence.build()
149176
const getResidenceDetailsSpy = jest

0 commit comments

Comments
 (0)