mirror of
https://github.com/inaturalist/iNaturalistReactNative.git
synced 2026-09-16 07:44:34 -04:00
295 lines
11 KiB
JavaScript
295 lines
11 KiB
JavaScript
import Observation from "realmModels/Observation";
|
|
import ObservationFieldValue from "realmModels/ObservationFieldValue";
|
|
import ProjectObservation from "realmModels/ProjectObservation";
|
|
import safeRealmWrite from "sharedHelpers/safeRealmWrite";
|
|
import factory from "tests/factory";
|
|
import * as uuid from "uuid";
|
|
|
|
describe( "Observation", ( ) => {
|
|
describe( "mapObservationForUpload", ( ) => {
|
|
// observed_on is set by the server, clients specify the date with observed_on_string
|
|
it( "should not include observed_on", ( ) => {
|
|
expect(
|
|
Observation.mapObservationForUpload( { observed_on: "2020-01-01" } ).observed_on,
|
|
).toBeUndefined( );
|
|
} );
|
|
} );
|
|
|
|
describe( "mapApiToRealm", ( ) => {
|
|
it(
|
|
"should assign user.prefers_community_taxa from user.preferences.prefers_community_taxa",
|
|
( ) => {
|
|
const mockApiObservation = {
|
|
user: {
|
|
preferences: {
|
|
prefers_community_taxa: false,
|
|
},
|
|
},
|
|
};
|
|
expect(
|
|
Observation.mapApiToRealm( mockApiObservation ).user.prefers_community_taxa,
|
|
).toEqual( mockApiObservation.user.preferences.prefers_community_taxa );
|
|
},
|
|
);
|
|
it( "should set _created_at to a date object without Realm", ( ) => {
|
|
expect( Observation.mapApiToRealm( { } )._created_at ).toBeInstanceOf( Date );
|
|
} );
|
|
it( "should create observationSounds from observation_sounds", ( ) => {
|
|
const remoteObservationSound = factory( "RemoteObservationSound" );
|
|
const mappedObservation = Observation.mapApiToRealm( {
|
|
observation_sounds: [remoteObservationSound],
|
|
} );
|
|
expect( mappedObservation.observationSounds[0].sound.file_url )
|
|
.toEqual( remoteObservationSound.sound.file_url );
|
|
expect( mappedObservation.observationSounds[0].uuid )
|
|
.toEqual( remoteObservationSound.uuid );
|
|
} );
|
|
|
|
it( "should map project_observations to projectObservations with created_at metadata", ( ) => {
|
|
const mockRemoteObservation = factory( "RemoteObservation", {
|
|
project_observations: [factory( "RemoteProjectObservation" )],
|
|
} );
|
|
const mappedObservation = Observation.mapApiToRealm( mockRemoteObservation );
|
|
expect( mappedObservation.projectObservations ).toHaveLength( 1 );
|
|
expect( mappedObservation.projectObservations[0]._created_at ).toBeInstanceOf( Date );
|
|
} );
|
|
|
|
it( "should map ofvs to observationFieldValues with created_at metadata", ( ) => {
|
|
const mockRemoteObservation = factory( "RemoteObservation", {
|
|
ofvs: [factory( "RemoteObservationFieldValue" )],
|
|
} );
|
|
const mappedObservation = Observation.mapApiToRealm( mockRemoteObservation );
|
|
expect( mappedObservation.observationFieldValues ).toHaveLength( 1 );
|
|
expect( mappedObservation.observationFieldValues[0]._created_at ).toBeInstanceOf( Date );
|
|
} );
|
|
} );
|
|
|
|
describe( "upsertRemoteObservations", ( ) => {
|
|
it( "should persist observationFieldValues in Realm", ( ) => {
|
|
const mockRemoteObservation = factory( "RemoteObservation", {
|
|
ofvs: [factory( "RemoteObservationFieldValue" )],
|
|
} );
|
|
|
|
Observation.upsertRemoteObservations( [mockRemoteObservation], global.realm );
|
|
|
|
const obs = global.realm.objectForPrimaryKey( "Observation", mockRemoteObservation.uuid );
|
|
expect( obs.observationFieldValues ).toHaveLength( 1 );
|
|
expect( obs.observationFieldValues[0].value ).toBe(
|
|
mockRemoteObservation.ofvs[0].value,
|
|
);
|
|
expect( obs.observationFieldValues[0].obsFieldId ).toBe(
|
|
mockRemoteObservation.ofvs[0].field_id,
|
|
);
|
|
} );
|
|
|
|
it( "should persist projectObservations in Realm", ( ) => {
|
|
const mockRemoteObservation = factory( "RemoteObservation", {
|
|
project_observations: [factory( "RemoteProjectObservation" )],
|
|
} );
|
|
|
|
Observation.upsertRemoteObservations( [mockRemoteObservation], global.realm );
|
|
|
|
const obs = global.realm.objectForPrimaryKey( "Observation", mockRemoteObservation.uuid );
|
|
expect( obs.projectObservations ).toHaveLength( 1 );
|
|
expect( obs.projectObservations[0].id ).toBe(
|
|
mockRemoteObservation.project_observations[0].id,
|
|
);
|
|
expect( obs.projectObservations[0].projectId ).toBe(
|
|
mockRemoteObservation.project_observations[0].project_id,
|
|
);
|
|
} );
|
|
} );
|
|
|
|
describe( "needsSync", ( ) => {
|
|
it.todo( "should need sync when a photo needs sync" );
|
|
it.todo( "should need sync when a sound needs sync" );
|
|
it( "should need sync when a project observation needs sync", ( ) => {
|
|
const obsUuid = uuid.v4( );
|
|
const syncDate = new Date( "2020-01-02" );
|
|
safeRealmWrite( global.realm, ( ) => {
|
|
global.realm.create( "Observation", {
|
|
uuid: obsUuid,
|
|
_synced_at: syncDate,
|
|
_updated_at: syncDate,
|
|
projectObservations: [ProjectObservation.new( 1 )],
|
|
} );
|
|
}, "create Observation with unsynced PO for needsSync test" );
|
|
|
|
const obs = global.realm.objectForPrimaryKey( "Observation", obsUuid );
|
|
expect( obs.needsSync( ) ).toBe( true );
|
|
} );
|
|
|
|
it( "should need sync when an observation field value needs sync", ( ) => {
|
|
const obsUuid = uuid.v4( );
|
|
const syncDate = new Date( "2020-01-02" );
|
|
safeRealmWrite( global.realm, ( ) => {
|
|
global.realm.create( "Observation", {
|
|
uuid: obsUuid,
|
|
_synced_at: syncDate,
|
|
_updated_at: syncDate,
|
|
observationFieldValues: [
|
|
ObservationFieldValue.new( 5, "x" ),
|
|
],
|
|
} );
|
|
}, "create Observation with unsynced OFV for needsSync test" );
|
|
|
|
const obs = global.realm.objectForPrimaryKey( "Observation", obsUuid );
|
|
expect( obs.needsSync( ) ).toBe( true );
|
|
} );
|
|
} );
|
|
|
|
describe( "filterUnsyncedObservations", ( ) => {
|
|
it( "should include observations with unsynced project observations", ( ) => {
|
|
const obsUuid = uuid.v4( );
|
|
const syncDate = new Date( "2020-01-02" );
|
|
safeRealmWrite( global.realm, ( ) => {
|
|
global.realm.create( "Observation", {
|
|
uuid: obsUuid,
|
|
_synced_at: syncDate,
|
|
_updated_at: syncDate,
|
|
projectObservations: [ProjectObservation.new( 1 )],
|
|
} );
|
|
}, "create synced obs with unsynced PO" );
|
|
|
|
const unsynced = Observation.filterUnsyncedObservations( global.realm );
|
|
expect( unsynced.filtered( `uuid == "${obsUuid}"` ).length ).toBe( 1 );
|
|
} );
|
|
|
|
it( "should include observations with unsynced observation field values", ( ) => {
|
|
const obsUuid = uuid.v4( );
|
|
const syncDate = new Date( "2020-01-02" );
|
|
safeRealmWrite( global.realm, ( ) => {
|
|
global.realm.create( "Observation", {
|
|
uuid: obsUuid,
|
|
_synced_at: syncDate,
|
|
_updated_at: syncDate,
|
|
observationFieldValues: [
|
|
ObservationFieldValue.new( 5, "x" ),
|
|
],
|
|
} );
|
|
}, "create synced obs with unsynced OFV" );
|
|
|
|
const unsynced = Observation.filterUnsyncedObservations( global.realm );
|
|
expect( unsynced.filtered( `uuid == "${obsUuid}"` ).length ).toBe( 1 );
|
|
} );
|
|
} );
|
|
|
|
describe( "saveLocalObservationForUpload", ( ) => {
|
|
it( "creates Realm tombstones from pending-removal POs", async ( ) => {
|
|
const obsUuid = uuid.v4( );
|
|
const syncedAt = new Date( "2020-01-02" );
|
|
const poUuid = uuid.v4( ).toLowerCase( );
|
|
|
|
const mockPO = factory( "LocalProjectObservation", {
|
|
uuid: poUuid,
|
|
_synced_at: syncedAt,
|
|
_pendingRemoval: true,
|
|
} );
|
|
await Observation.saveLocalObservationForUpload( {
|
|
uuid: obsUuid,
|
|
projectObservations: [mockPO],
|
|
observationFieldValues: [],
|
|
observationPhotos: [],
|
|
observationSounds: [],
|
|
}, global.realm );
|
|
|
|
const obs = global.realm.objectForPrimaryKey( "Observation", obsUuid );
|
|
expect( obs.projectObservations ).toHaveLength( 1 );
|
|
expect( obs.projectObservations[0]._pending_deletion ).toBe( true );
|
|
expect( obs.projectObservations[0].uuid ).toBe( poUuid );
|
|
expect( obs.projectObservations[0]._synced_at ).toEqual( syncedAt );
|
|
} );
|
|
|
|
it( "creates Realm tombstones from pending-removal OFVs", async ( ) => {
|
|
const obsUuid = uuid.v4( );
|
|
const syncedAt = new Date( "2020-01-02" );
|
|
const ofvUuid = uuid.v4( ).toLowerCase( );
|
|
|
|
const mockOFV = factory( "LocalObservationFieldValue", {
|
|
uuid: ofvUuid,
|
|
_synced_at: syncedAt,
|
|
_pendingRemoval: true,
|
|
} );
|
|
await Observation.saveLocalObservationForUpload( {
|
|
uuid: obsUuid,
|
|
projectObservations: [],
|
|
observationFieldValues: [mockOFV],
|
|
observationPhotos: [],
|
|
observationSounds: [],
|
|
}, global.realm );
|
|
|
|
const obs = global.realm.objectForPrimaryKey( "Observation", obsUuid );
|
|
expect( obs.observationFieldValues ).toHaveLength( 1 );
|
|
expect( obs.observationFieldValues[0]._pending_deletion ).toBe( true );
|
|
expect( obs.observationFieldValues[0].uuid ).toBe( ofvUuid );
|
|
} );
|
|
|
|
it( "writes re-selected POs as active embeds without tombstones", async ( ) => {
|
|
const obsUuid = uuid.v4( );
|
|
const syncedAt = new Date( "2020-01-02" );
|
|
const poUuid = uuid.v4( ).toLowerCase( );
|
|
|
|
const mockPO = factory( "LocalProjectObservation", {
|
|
uuid: poUuid,
|
|
_synced_at: syncedAt,
|
|
_pending_deletion: true,
|
|
} );
|
|
|
|
safeRealmWrite( global.realm, ( ) => {
|
|
global.realm.create( "Observation", {
|
|
uuid: obsUuid,
|
|
_synced_at: syncedAt,
|
|
_updated_at: syncedAt,
|
|
projectObservations: [mockPO],
|
|
} );
|
|
}, "seed tombstoned PO for re-select save test" );
|
|
|
|
delete mockPO._pending_deletion;
|
|
await Observation.saveLocalObservationForUpload( {
|
|
uuid: obsUuid,
|
|
projectObservations: [mockPO],
|
|
observationFieldValues: [],
|
|
observationPhotos: [],
|
|
observationSounds: [],
|
|
}, global.realm );
|
|
|
|
const obs = global.realm.objectForPrimaryKey( "Observation", obsUuid );
|
|
expect( obs.projectObservations[0]._pending_deletion ).toBeFalsy( );
|
|
expect( obs.projectObservations[0]._synced_at ).toBeNull( );
|
|
} );
|
|
|
|
it( "leaves unchanged synced embed timestamps intact on re-edit", async ( ) => {
|
|
const obsUuid = uuid.v4( );
|
|
const syncedAt = new Date( "2020-01-02" );
|
|
const poUuid = uuid.v4( ).toLowerCase( );
|
|
const ofvUuid = uuid.v4( ).toLowerCase( );
|
|
|
|
const mockPO = factory( "LocalProjectObservation", {
|
|
uuid: poUuid,
|
|
_synced_at: syncedAt,
|
|
} );
|
|
const mockOFV = factory( "LocalObservationFieldValue", {
|
|
uuid: ofvUuid,
|
|
_synced_at: syncedAt,
|
|
} );
|
|
const mockObs = factory( "LocalObservation", {
|
|
uuid: obsUuid,
|
|
projectObservations: [mockPO],
|
|
observationFieldValues: [mockOFV],
|
|
observationPhotos: [],
|
|
observationSounds: [],
|
|
} );
|
|
|
|
safeRealmWrite( global.realm, ( ) => {
|
|
global.realm.create( "Observation", mockObs );
|
|
}, "seed synced PO/OFV for unchanged re-edit save test" );
|
|
|
|
await Observation.saveLocalObservationForUpload( mockObs, global.realm );
|
|
|
|
const obs = global.realm.objectForPrimaryKey( "Observation", obsUuid );
|
|
expect( obs.projectObservations[0]._synced_at ).toEqual( syncedAt );
|
|
expect( obs.observationFieldValues[0]._synced_at ).toEqual( syncedAt );
|
|
} );
|
|
} );
|
|
} );
|