1 /** 2 Contains a completely in-memory database. 3 */ 4 module ezdb.driver.memory; 5 6 import ezdb.repository; 7 import ezdb.entity; 8 9 import optional; 10 11 import std.exception; 12 13 /** 14 An in-memory database, useful for unit-testing. 15 This memory does not need special cleanup. 16 17 Note that this database is not optimised for high performance, it should only 18 be used for small amounts of data. 19 */ 20 class MemoryDriver(Db : Repository!Entity, Entity) : Db 21 { 22 private Entity[PrimaryKeyType!Entity] _entities; 23 private PrimaryKeyType!Entity _nextId = 1; 24 25 override void close() 26 { 27 } 28 29 override Entity save(Entity entity) 30 { 31 setPrimaryKey(entity, _nextId); 32 _entities[getPrimaryKey(entity)] = entity; 33 _nextId++; 34 return entity; 35 } 36 37 override Optional!Entity find(PrimaryKeyType!Entity id) 38 { 39 if (id !in _entities) 40 return no!Entity; 41 return some(_entities[id]); 42 } 43 44 override Entity[] findAll() 45 { 46 Entity[] entities; 47 foreach (_, entity; _entities) 48 entities ~= entity; 49 return entities; 50 } 51 52 override void remove(PrimaryKeyType!Entity id) 53 { 54 _entities.remove(id); 55 } 56 } 57 58 @("Can create a Memory database") 59 unittest 60 { 61 static struct Entity 62 { 63 @primaryKey int id; 64 } 65 static interface Repo : Repository!Entity {} 66 auto db = new MemoryDriver!Repo; 67 scope(exit) db.close(); 68 assert(db !is null); 69 } 70 71 @("Empty database should return no results") 72 unittest 73 { 74 static struct Entity 75 { 76 @primaryKey int id; 77 } 78 static interface Repo : Repository!Entity {} 79 auto db = new MemoryDriver!Repo; 80 scope(exit) db.close(); 81 assert(db.findAll() == [], "findAll() should return an empty list of the database is empty"); 82 } 83 84 @("Save() should return a saved instance") 85 unittest 86 { 87 static struct Entity 88 { 89 @primaryKey int id; 90 int value; 91 } 92 static interface Repo : Repository!Entity {} 93 auto db = new MemoryDriver!Repo; 94 scope(exit) db.close(); 95 96 Entity toSave; 97 toSave.value = 5; 98 99 const saved1 = db.save(toSave); 100 assert(saved1.value == 5, "Entity.value was not correctly saved"); 101 assert(saved1.id == 1, "Entity.id was not generated"); 102 103 const saved2 = db.save(toSave); 104 assert(saved2.value == 5, "Entity.value was not correctly saved"); 105 assert(saved2.id == 2, "Entity.id was not generated"); 106 } 107 108 @("findAll() should return all instances when saved") 109 unittest 110 { 111 static struct Entity 112 { 113 @primaryKey int id; 114 int value; 115 } 116 static interface Repo : Repository!Entity {} 117 auto db = new MemoryDriver!Repo; 118 scope(exit) db.close(); 119 120 Entity toSave; 121 toSave.value = 5; 122 123 const saved = db.save(toSave); 124 125 assert(db.findAll() == [saved], "Did not correctly retrieve all results"); 126 } 127 128 @("remove() should remove an instance") 129 unittest 130 { 131 static struct Entity 132 { 133 @primaryKey int id; 134 int value; 135 } 136 static interface Repo : Repository!Entity {} 137 auto db = new MemoryDriver!Repo; 138 scope(exit) db.close(); 139 140 Entity toSave; 141 const saved = db.save(toSave); 142 db.remove(saved.id); 143 }