import { getStreetsCollection, getStreetsByCityName, getStreetById, getStreetByStreetId, getStreetByStreetIdAndCity, createStreet, bulkWriteStreets, } from '../streets.getters'; import { createStreet as streetFactory } from '../../../test-utils/factories/municipal/streets'; const CITY = 'tests' as const; describe('Street — isActive field', () => { describe('persistence', () => { it('a document inserted without isActive is retrievable and the field is absent', async () => { const street = streetFactory({ cityName: CITY }); delete (street as Partial).isActive; await getStreetsCollection().insertOne(street); const saved = await getStreetsCollection().findOne({ _id: street._id }); expect(saved).not.toBeNull(); expect(saved).not.toHaveProperty('isActive'); }); it('isActive: true is persisted and returned', async () => { const street = streetFactory({ cityName: CITY, isActive: true }); await getStreetsCollection().insertOne(street); const saved = await getStreetsCollection().findOne({ _id: street._id }); expect(saved?.isActive).toBe(true); }); it('isActive: false is persisted and returned', async () => { const street = streetFactory({ cityName: CITY, isActive: false }); await getStreetsCollection().insertOne(street); const saved = await getStreetsCollection().findOne({ _id: street._id }); expect(saved?.isActive).toBe(false); }); }); describe('createStreet', () => { it('defaults isActive to true when not provided', async () => { const data = streetFactory({ cityName: CITY }); delete (data as Partial).isActive; const insertedId = await createStreet(data); const saved = await getStreetsCollection().findOne({ _id: insertedId }); expect(saved?.isActive).toBe(true); }); it('persists isActive: false when explicitly passed', async () => { const data = streetFactory({ cityName: CITY, isActive: false }); const insertedId = await createStreet(data); const saved = await getStreetsCollection().findOne({ _id: insertedId }); expect(saved?.isActive).toBe(false); }); }); describe('querying', () => { it('getStreetsByCityName returns all streets regardless of isActive', async () => { const active = streetFactory({ cityName: CITY, isActive: true }); const inactive = streetFactory({ cityName: CITY, isActive: false }); const noFlag = streetFactory({ cityName: CITY }); delete (noFlag as Partial).isActive; await getStreetsCollection().insertMany([active, inactive, noFlag]); const all = await getStreetsByCityName(CITY); expect(all.length).toBeGreaterThanOrEqual(3); const returnedActive = all.find((s) => s._id.equals(active._id)); const returnedInactive = all.find((s) => s._id.equals(inactive._id)); const returnedNoFlag = all.find((s) => s._id.equals(noFlag._id)); expect(returnedActive?.isActive).toBe(true); expect(returnedInactive?.isActive).toBe(false); expect(returnedNoFlag?.isActive).toBeUndefined(); // absent in DB → getter is a pass-through, no normalization }); it('getStreetById returns isActive undefined when absent in DB', async () => { const street = streetFactory({ cityName: CITY }); delete (street as Partial).isActive; await getStreetsCollection().insertOne(street); const result = await getStreetById(street._id.toString()); expect(result?.isActive).toBeUndefined(); }); it('getStreetById returns isActive: false unchanged', async () => { const street = streetFactory({ cityName: CITY, isActive: false }); await getStreetsCollection().insertOne(street); const result = await getStreetById(street._id.toString()); expect(result?.isActive).toBe(false); }); it('getStreetByStreetId returns isActive undefined when absent in DB', async () => { const street = streetFactory({ cityName: CITY }); delete (street as Partial).isActive; await getStreetsCollection().insertOne(street); const result = await getStreetByStreetId(street.id); expect(result?.isActive).toBeUndefined(); }); it('getStreetByStreetIdAndCity returns isActive undefined when absent in DB', async () => { const street = streetFactory({ cityName: CITY }); delete (street as Partial).isActive; await getStreetsCollection().insertOne(street); const result = await getStreetByStreetIdAndCity(street.id, CITY); expect(result?.isActive).toBeUndefined(); }); it('filtering with isActive !== false excludes inactive and includes active and unset documents', async () => { const active = streetFactory({ cityName: CITY, isActive: true }); const inactive = streetFactory({ cityName: CITY, isActive: false }); const noFlag = streetFactory({ cityName: CITY }); delete (noFlag as Partial).isActive; await getStreetsCollection().insertMany([active, inactive, noFlag]); // This is the exact filter pattern used by the sync script to skip already-inactive records const notInactive = await getStreetsCollection() .find({ cityName: CITY, isActive: { $ne: false } }) .toArray(); expect(notInactive.map((s) => s._id.toString())).toEqual( expect.arrayContaining([active._id.toString(), noFlag._id.toString()]) ); expect(notInactive.map((s) => s._id.toString())).not.toContain(inactive._id.toString()); }); }); describe('updating', () => { it('isActive can be set to false on an existing active document (deactivation)', async () => { const street = streetFactory({ cityName: CITY, isActive: true }); await getStreetsCollection().insertOne(street); await getStreetsCollection().updateOne( { _id: street._id }, { $set: { isActive: false, updatedAt: new Date() } } ); const updated = await getStreetsCollection().findOne({ _id: street._id }); expect(updated?.isActive).toBe(false); }); it('isActive can be set to true on a deactivated document (reactivation)', async () => { const street = streetFactory({ cityName: CITY, isActive: false }); await getStreetsCollection().insertOne(street); await getStreetsCollection().updateOne( { _id: street._id }, { $set: { isActive: true, updatedAt: new Date() } } ); const updated = await getStreetsCollection().findOne({ _id: street._id }); expect(updated?.isActive).toBe(true); }); it('deactivating a street does not touch other fields', async () => { const street = streetFactory({ cityName: CITY, isActive: true, name: 'Herzl St', hints: [{ id: '1', text: 'near the park' }], }); await getStreetsCollection().insertOne(street); await getStreetsCollection().updateOne( { _id: street._id }, { $set: { isActive: false, updatedAt: new Date() } } ); const updated = await getStreetsCollection().findOne({ _id: street._id }); expect(updated?.isActive).toBe(false); expect(updated?.name).toBe('Herzl St'); expect(updated?.hints).toEqual([{ id: '1', text: 'near the park' }]); }); }); describe('bulkWriteStreets', () => { it('applies upsert and update operations from a single batch', async () => { const existing = streetFactory({ cityName: CITY, name: 'Old Name' }); await getStreetsCollection().insertOne(existing); const result = await bulkWriteStreets( [ { updateOne: { filter: { _id: existing._id }, update: { $set: { name: 'New Name' } }, }, }, { updateOne: { filter: { id: 'brand-new-street', cityName: CITY }, update: { $set: { name: 'Brand New St', cityName: CITY }, $setOnInsert: { id: 'brand-new-street', createdAt: new Date(), isActive: true }, }, upsert: true, }, }, ], { ordered: false } ); expect(result.modifiedCount).toBe(1); expect(result.upsertedCount).toBe(1); const updated = await getStreetsCollection().findOne({ _id: existing._id }); expect(updated?.name).toBe('New Name'); const inserted = await getStreetsCollection().findOne({ id: 'brand-new-street', cityName: CITY }); expect(inserted?.name).toBe('Brand New St'); }); it('propagates a MongoBulkWriteError for an invalid operation, preserving partial results', async () => { const existing = streetFactory({ cityName: CITY }); await getStreetsCollection().insertOne(existing); await expect( bulkWriteStreets( [ { updateOne: { filter: { _id: existing._id }, update: { $set: { name: 'Valid Update' } }, }, }, { insertOne: { // Duplicate _id triggers a write error without aborting the ordered:false batch document: { ...existing }, }, }, ], { ordered: false } ) ).rejects.toMatchObject({ name: 'MongoBulkWriteError' }); const updated = await getStreetsCollection().findOne({ _id: existing._id }); expect(updated?.name).toBe('Valid Update'); }); }); });