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 }