Update model unit tests.

Commit
f1c9fd9dd6fe13fc175dc129599998ee3a711bff
Author
Marius Peter <dev@marius-peter.com>
Author date
Committer
Marius Peter <dev@marius-peter.com>
Committer date
Changed files
models/crop-requirement.rkt
index 2dd8071a..dc3f3a0a 100644..100644
@@ -189,23 +189,23 @@
189 189 "../models/nutrient.rkt"
190 190 "../models/crop.rkt")
191 191
192 Removed: (define requirement-profile "Tomato - Vegetative")
192 Added: (define requirement-profile "Examplium Plant - Vegetative")
193 193
194 194 (run-tests
195 195 (test-suite "Crop requirement model"
196 196 #:before (λ ()
197 197 (connect! #:path 'memory)
198 198 (migrate-all!)
199 Removed: (create-nutrient! "Nitrogen" "Azote" "N")
200 Removed: (create-nutrient! "Phosphorus" "Phosphore" "P")
201 Removed: (create-nutrient! "Potassium" "Potassium" "K"))
199 Added: (create-nutrient! "Examplium" "Examplium" "Ex")
200 Added: (create-nutrient! "Ignorium" "Ignorium" "Ig")
201 Added: (create-nutrient! "Testium" "Testium" "Ts"))
202 202 #:after (λ () (disconnect!))
203 203
204 204 (test-case "Create requirement with profile and values"
205 Removed: (define nitrogen (get-nutrient #:name "Nitrogen"))
206 Removed: (define phosphorus (get-nutrient #:name "Phosphorus"))
205 Added: (define examplium (get-nutrient #:name "Examplium"))
206 Added: (define ignorium (get-nutrient #:name "Ignorium"))
207 207
208 Removed: (create-crop-requirement! requirement-profile (hash nitrogen 150 phosphorus 50))
208 Added: (create-crop-requirement! requirement-profile (hash examplium 150 ignorium 50))
209 209
210 210 (check-equal? (length (get-crop-requirements)) 1)
211 211
@@ -214,21 +214,22 @@
214 214 (check-equal? (crop-requirement-profile cr) requirement-profile))
215 215
216 216 (test-case "Create requirement with associated crop"
217 Removed: (define tomato (create-crop! "Tomato"))
218 Removed: (define nitrogen (get-nutrient #:name "Nitrogen"))
217 Added: (define test-crop (create-crop! (crop #f "testium-plant")))
218 Added: (define examplium (get-nutrient #:name "Examplium"))
219 219
220 Removed: (define cr (create-crop-requirement! "Tomato - Fruiting" (hash nitrogen 200) tomato))
220 Added: (define cr
221 Added: (create-crop-requirement! "Testium Plant - Fruiting" (hash examplium 200) test-crop))
221 222
222 Removed: (check-equal? (crop-requirement-crop-id cr) (crop-id tomato)))
223 Added: (check-equal? (crop-requirement-crop-id cr) (crop-id test-crop)))
223 224
224 225 (test-case "Check all requirement values"
225 Removed: (define nitrogen (get-nutrient #:name "Nitrogen"))
226 Removed: (define phosphorus (get-nutrient #:name "Phosphorus"))
226 Added: (define examplium (get-nutrient #:name "Examplium"))
227 Added: (define ignorium (get-nutrient #:name "Ignorium"))
227 228
228 229 (define cr (get-crop-requirement #:profile requirement-profile))
229 230
230 Removed: (check-= (get-crop-requirement-value cr nitrogen) 150 0)
231 Removed: (check-= (get-crop-requirement-value cr phosphorus) 50 0)
231 Added: (check-= (get-crop-requirement-value cr examplium) 150 0)
232 Added: (check-= (get-crop-requirement-value cr ignorium) 50 0)
232 233
233 234 (define crv (crop-requirement-nutrient-values cr))
234 235
@@ -238,8 +239,8 @@
238 239 "return value of get-crop-requirement-values ≠ crop-requirement-values struct accessor")
239 240
240 241 (check-equal? (hash-count crv) 2)
241 Removed: (check-= (hash-ref crv nitrogen) 150 0)
242 Removed: (check-= (hash-ref crv phosphorus) 50 0))
242 Added: (check-= (hash-ref crv examplium) 150 0)
243 Added: (check-= (hash-ref crv ignorium) 50 0))
243 244
244 245 (test-case "Get requirement by id"
245 246 (define cr (get-crop-requirement #:profile requirement-profile))
@@ -248,19 +249,19 @@
248 249 (check-equal? cr cr-by-id))
249 250
250 251 (test-case "Average crop requirement nutrient values"
251 Removed: (define nitrogen (get-nutrient #:name "Nitrogen"))
252 Removed: (define phosphorus (get-nutrient #:name "Phosphorus"))
252 Added: (define examplium (get-nutrient #:name "Examplium"))
253 Added: (define ignorium (get-nutrient #:name "Ignorium"))
253 254
254 255 (define cr1 (get-crop-requirement #:profile requirement-profile))
255 Removed: (define cr2 (create-crop-requirement! "Lettuce" (hash nitrogen 100 phosphorus 30)))
256 Added: (define cr2 (create-crop-requirement! "Generic Growth" (hash examplium 100 ignorium 30)))
256 257
257 258 (define mix (hash cr1 60 cr2 40))
258 259 (define avg (average-crop-requirement-nutrient-values mix))
259 260
260 261 ;; 150 * 0.6 + 100 * 0.4 = 90 + 40 = 130
261 Removed: (check-= (hash-ref avg nitrogen) 130 0.01)
262 Added: (check-= (hash-ref avg examplium) 130 0.01)
262 263 ;; 50 * 0.6 + 30 * 0.4 = 30 + 12 = 42
263 Removed: (check-= (hash-ref avg phosphorus) 42 0.01))
264 Added: (check-= (hash-ref avg ignorium) 42 0.01))
264 265
265 266 (test-case "Delete requirement and cascade to requirement values"
266 267 (define cr (get-crop-requirement #:profile requirement-profile))
models/crop-rotation.rkt
index 3b0a69dc..fb7775f3 100644..100644
@@ -3,6 +3,7 @@
3 3 (provide crop-rotation
4 4 crop-rotation?
5 5 crop-rotation-id
6 Added: crop-rotation-requirement
6 7 (rename-out [crop-rotation-rotation-date crop-rotation-date]
7 8 [crop-rotation-requirement-proportions crop-rotation-requirements])
8 9 (contract-out
@@ -20,6 +21,9 @@
20 21
21 22 (struct crop-rotation (id rotation-date requirement-proportions) #:transparent)
22 23
24 Added: (define (crop-rotation-requirement cr requirement)
25 Added: (hash-ref (crop-rotation-requirement-proportions cr) requirement #f))
26 Added:
23 27 (define crop-rotation-or-id/c (or/c crop-rotation? db-id?))
24 28 (define requirement-proportion-hash/c (hash/c crop-requirement? (between/c 0 100) #:immutable #t))
25 29
@@ -112,3 +116,157 @@
112 116
113 117 (define (delete-crop-rotation! cr-or-id)
114 118 (query-exec (current-conn) (delete #:from crop_rotations #:where (= id ,(->cr-id cr-or-id)))))
119 Added:
120 Added: (module+ test
121 Added: (require rackunit
122 Added: rackunit/text-ui
123 Added: "../db/conn.rkt"
124 Added: "../db/migrations.rkt"
125 Added: "../models/nutrient.rkt"
126 Added: "../models/crop.rkt"
127 Added: "../models/crop-requirement.rkt")
128 Added:
129 Added: (define rotation-date-1 "2025-01-15")
130 Added: (define rotation-date-2 "2025-02-20")
131 Added:
132 Added: (run-tests
133 Added: (test-suite "Crop rotation model"
134 Added: #:before (λ ()
135 Added: (connect! #:path 'memory)
136 Added: (migrate-all!)
137 Added: (create-nutrient! "Examplium" "Examplium" "Ex")
138 Added: (create-nutrient! "Ignorium" "Ignorium" "Ig")
139 Added: (create-nutrient! "Testium" "Testium" "Ts"))
140 Added: #:after (λ () (disconnect!))
141 Added:
142 Added: (test-case "Create crop rotation with requirement proportions"
143 Added: (define ex (get-nutrient #:name "Examplium"))
144 Added: (define ig (get-nutrient #:name "Ignorium"))
145 Added:
146 Added: (define req1 (create-crop-requirement! "vegetative" (hash ex 100 ig 50)))
147 Added: (define req2 (create-crop-requirement! "fruiting" (hash ex 150 ig 75)))
148 Added:
149 Added: (define proportions (hash req1 60 req2 40))
150 Added: (define rotation (create-crop-rotation! rotation-date-1 proportions))
151 Added:
152 Added: (check-true (crop-rotation? rotation))
153 Added: (check-equal? (crop-rotation-rotation-date rotation) rotation-date-1)
154 Added: (check-equal? (hash-count (crop-rotation-requirement-proportions rotation)) 2)
155 Added:
156 Added: ;; Find requirements in the returned hash by profile
157 Added: (define reqs (crop-rotation-requirement-proportions rotation))
158 Added: (define req1-found
159 Added: (findf (λ (r) (equal? (crop-requirement-profile r) "vegetative")) (hash-keys reqs)))
160 Added: (define req2-found
161 Added: (findf (λ (r) (equal? (crop-requirement-profile r) "fruiting")) (hash-keys reqs)))
162 Added:
163 Added: (check-equal? (hash-ref reqs req1-found) 60)
164 Added: (check-equal? (hash-ref reqs req2-found) 40))
165 Added:
166 Added: (test-case "Create duplicate rotation returns existing"
167 Added: ;; Get all requirements to find the ones we need
168 Added: (define all-reqs (get-crop-requirements))
169 Added: (define req1 (findf (λ (r) (equal? (crop-requirement-profile r) "vegetative")) all-reqs))
170 Added: (define req2 (findf (λ (r) (equal? (crop-requirement-profile r) "fruiting")) all-reqs))
171 Added:
172 Added: ;; Try to create with different proportions - should return existing
173 Added: (define proportions (hash req1 70 req2 30))
174 Added: (define rotation2 (create-crop-rotation! rotation-date-1 proportions))
175 Added:
176 Added: (check-equal? (length (get-crop-rotations)) 1)
177 Added:
178 Added: ;; The returned rotation should have original proportions (60/40), not new ones (70/30)
179 Added: (define reqs2 (crop-rotation-requirement-proportions rotation2))
180 Added: (define req1-from-rotation
181 Added: (findf (λ (r) (equal? (crop-requirement-profile r) "vegetative")) (hash-keys reqs2)))
182 Added: (define req2-from-rotation
183 Added: (findf (λ (r) (equal? (crop-requirement-profile r) "fruiting")) (hash-keys reqs2)))
184 Added:
185 Added: (check-equal? (hash-ref reqs2 req1-from-rotation) 60)
186 Added: (check-equal? (hash-ref reqs2 req2-from-rotation) 40))
187 Added:
188 Added: (test-case "Get crop rotation by id"
189 Added: (define rotation1 (get-crop-rotation #:date rotation-date-1))
190 Added: (define rotation2 (get-crop-rotation #:id (crop-rotation-id rotation1)))
191 Added: (check-equal? (crop-rotation-id rotation1) (crop-rotation-id rotation2))
192 Added: (check-equal? (crop-rotation-rotation-date rotation1) (crop-rotation-rotation-date rotation2)))
193 Added:
194 Added: (test-case "Get crop rotation by date"
195 Added: (define rotation (get-crop-rotation #:date rotation-date-1))
196 Added: (check-true (crop-rotation? rotation))
197 Added: (check-equal? (crop-rotation-rotation-date rotation) rotation-date-1))
198 Added:
199 Added: (test-case "Get crop rotation with both id and date"
200 Added: (define rotation1 (get-crop-rotation #:date rotation-date-1))
201 Added: (define rotation2 (get-crop-rotation #:id (crop-rotation-id rotation1) #:date rotation-date-1))
202 Added: (check-equal? (crop-rotation-id rotation1) (crop-rotation-id rotation2)))
203 Added:
204 Added: (test-case "Get non-existent crop rotation"
205 Added: (check-false (get-crop-rotation #:date "2099-12-31"))
206 Added: (check-false (get-crop-rotation #:id 9999)))
207 Added:
208 Added: (test-case "Get all crop rotations ordered by date descending"
209 Added: (define ts (get-nutrient #:name "Testium"))
210 Added: (define req3 (create-crop-requirement! "maintenance" (hash ts 80)))
211 Added: (create-crop-rotation! rotation-date-2 (hash req3 100))
212 Added:
213 Added: (define rotations (get-crop-rotations))
214 Added: (check-equal? (length rotations) 2)
215 Added: ;; Most recent first
216 Added: (check-equal? (crop-rotation-rotation-date (first rotations)) rotation-date-2)
217 Added: (check-equal? (crop-rotation-rotation-date (second rotations)) rotation-date-1))
218 Added:
219 Added: (test-case "Get latest crop rotation"
220 Added: (define latest (get-latest-crop-rotation))
221 Added: (check-true (crop-rotation? latest))
222 Added: (check-equal? (crop-rotation-rotation-date latest) rotation-date-2))
223 Added:
224 Added: (test-case "Get latest returns #f when no rotations exist"
225 Added: (for ([rotation (get-crop-rotations)])
226 Added: (delete-crop-rotation! (crop-rotation-id rotation)))
227 Added: (check-false (get-latest-crop-rotation)))
228 Added:
229 Added: (test-case "Delete crop rotation by struct"
230 Added: (define ts (get-nutrient #:name "Testium"))
231 Added: (define req
232 Added: (findf (λ (r) (equal? (crop-requirement-profile r) "maintenance")) (get-crop-requirements)))
233 Added: (create-crop-rotation! "2025-03-01" (hash req 100))
234 Added:
235 Added: (define rotation (get-crop-rotation #:date "2025-03-01"))
236 Added: (delete-crop-rotation! rotation)
237 Added: (check-false (get-crop-rotation #:id (crop-rotation-id rotation))))
238 Added:
239 Added: (test-case "Delete crop rotation by id"
240 Added: (define ts (get-nutrient #:name "Testium"))
241 Added: (define req
242 Added: (findf (λ (r) (equal? (crop-requirement-profile r) "maintenance")) (get-crop-requirements)))
243 Added: (create-crop-rotation! "2025-04-01" (hash req 100))
244 Added:
245 Added: (define rotation (get-crop-rotation #:date "2025-04-01"))
246 Added: (define rotation-id-val (crop-rotation-id rotation))
247 Added: (delete-crop-rotation! rotation-id-val)
248 Added: (check-false (get-crop-rotation #:id rotation-id-val)))
249 Added:
250 Added: (test-case "Delete crop rotation cascades to crop_rotation_requirements"
251 Added: (define ex (get-nutrient #:name "Examplium"))
252 Added: (define req1 (create-crop-requirement! "test-profile-1" (hash ex 100)))
253 Added: (define req2 (create-crop-requirement! "test-profile-2" (hash ex 200)))
254 Added: (create-crop-rotation! "2025-05-01" (hash req1 50 req2 50))
255 Added:
256 Added: (define initial-count (length (get-crop-rotations)))
257 Added: (define rotation (get-crop-rotation #:date "2025-05-01"))
258 Added: (delete-crop-rotation! rotation)
259 Added: (check-equal? (length (get-crop-rotations)) (- initial-count 1)))
260 Added:
261 Added: (test-case "Rotation with associated crop requirement"
262 Added: (define ex (get-nutrient #:name "Examplium"))
263 Added: (define test-crop (create-crop! (crop #f "test-crop-for-rotation")))
264 Added: (define req-with-crop (create-crop-requirement! "crop-specific" (hash ex 120) test-crop))
265 Added: (define rotation (create-crop-rotation! "2025-06-01" (hash req-with-crop 100)))
266 Added:
267 Added: (check-equal? (crop-requirement-crop-id req-with-crop) (crop-id test-crop))
268 Added: (define retrieved (get-crop-rotation #:date "2025-06-01"))
269 Added: (check-equal? (hash-count (crop-rotation-requirement-proportions retrieved)) 1))
270 Added:
271 Added: (test-case "Error when neither id nor date provided"
272 Added: (check-exn exn:fail? (λ () (get-crop-rotation)))))))
models/crop.rkt
index ba93550c..44305438 100644..100644
@@ -61,3 +61,84 @@
61 61
62 62 (define (delete-crop! id)
63 63 (query-exec (current-conn) (delete #:from crops #:where (= id ,id))))
64 Added:
65 Added: (module+ test
66 Added: (require rackunit
67 Added: rackunit/text-ui
68 Added: "../db/conn.rkt"
69 Added: "../db/migrations.rkt")
70 Added:
71 Added: (define test-crop-name "examplium-plant")
72 Added:
73 Added: (run-tests (test-suite "Crop model"
74 Added: #:before (λ ()
75 Added: (connect! #:path 'memory)
76 Added: (migrate-all!))
77 Added: #:after (λ () (disconnect!))
78 Added:
79 Added: (test-case "Create crop"
80 Added: (check-equal? (length (get-crops)) 0)
81 Added: (define c1 (create-crop! (crop #f test-crop-name)))
82 Added: (check-equal? (length (get-crops)) 1)
83 Added: (check-true (crop? c1))
84 Added: (check-equal? (crop-name c1) test-crop-name)
85 Added: (check-true (db-id? (crop-id c1))))
86 Added:
87 Added: (test-case "Create duplicate crop returns existing"
88 Added: (define c1 (create-crop! (crop #f test-crop-name)))
89 Added: (define c2 (create-crop! (crop #f test-crop-name)))
90 Added: (check-equal? (length (get-crops)) 1)
91 Added: (check-equal? (crop-id c1) (crop-id c2))
92 Added: (check-equal? c1 c2))
93 Added:
94 Added: (test-case "Get crop by id"
95 Added: (define c1 (get-crop #:name test-crop-name))
96 Added: (define c2 (get-crop #:id (crop-id c1)))
97 Added: (check-equal? c1 c2))
98 Added:
99 Added: (test-case "Get crop by name"
100 Added: (define c (get-crop #:name test-crop-name))
101 Added: (check-true (crop? c))
102 Added: (check-equal? (crop-name c) test-crop-name))
103 Added:
104 Added: (test-case "Get crop with both id and name"
105 Added: (define c1 (get-crop #:name test-crop-name))
106 Added: (define c2 (get-crop #:id (crop-id c1) #:name test-crop-name))
107 Added: (check-equal? c1 c2))
108 Added:
109 Added: (test-case "Get non-existent crop"
110 Added: (check-false (get-crop #:name "non-existent-crop"))
111 Added: (check-false (get-crop #:id 9999)))
112 Added:
113 Added: (test-case "Get all crops"
114 Added: (create-crop! (crop #f "ignorium-plant"))
115 Added: (create-crop! (crop #f "testium-plant"))
116 Added: (define crops (get-crops))
117 Added: (check-equal? (length crops) 3)
118 Added: (check-true (andmap crop? crops)))
119 Added:
120 Added: (test-case "Update crop name"
121 Added: (define c1 (get-crop #:name test-crop-name))
122 Added: (define updated-crop (crop (crop-id c1) "examplium-updated"))
123 Added: (update-crop! updated-crop)
124 Added: (define c2 (get-crop #:id (crop-id c1)))
125 Added: (check-equal? (crop-name c2) "examplium-updated")
126 Added: (check-equal? (crop-id c1) (crop-id c2))
127 Added: (check-equal? (length (get-crops)) 3))
128 Added:
129 Added: (test-case "Update crop with invalid id raises error"
130 Added: (define invalid-crop (crop #f "invalid"))
131 Added: (check-exn exn:fail:contract? (λ () (update-crop! invalid-crop))))
132 Added:
133 Added: (test-case "Delete crop"
134 Added: (define c (get-crop #:name "ignorium-plant"))
135 Added: (delete-crop! (crop-id c))
136 Added: (check-false (get-crop #:id (crop-id c)))
137 Added: (check-equal? (length (get-crops)) 2))
138 Added:
139 Added: (test-case "Delete crop by id"
140 Added: (define c (get-crop #:name "testium-plant"))
141 Added: (define crop-id-val (crop-id c))
142 Added: (delete-crop! crop-id-val)
143 Added: (check-false (get-crop #:id crop-id-val))
144 Added: (check-equal? (length (get-crops)) 1)))))
models/fertilizer-product.rkt
index 652b3c42..5d81681b 100644..100644
@@ -189,41 +189,41 @@
189 189 "../db/migrations.rkt"
190 190 "../models/nutrient.rkt")
191 191
192 Removed: (define canonical-product-name "MasterBlend 4-20")
192 Added: (define canonical-product-name "ExampleBlend 4-20")
193 193
194 194 (run-tests
195 195 (test-suite "Fertilizer product model"
196 196 #:before (λ ()
197 197 (connect! #:path 'memory)
198 198 (migrate-all!)
199 Removed: (create-nutrient! "Nitrogen" "Azote" "N")
200 Removed: (create-nutrient! "Phosphorus" "Phosphore" "P")
201 Removed: (create-nutrient! "Potassium" "Potassium" "K"))
199 Added: (create-nutrient! "Examplium" "Examplium" "Ex")
200 Added: (create-nutrient! "Ignorium" "Ignorium" "Ig")
201 Added: (create-nutrient! "Testium" "Testium" "Ts"))
202 202 #:after (λ () (disconnect!))
203 203
204 204 (test-case "Create product with name, brand, and values"
205 Removed: (define nitrogen (get-nutrient #:name "Nitrogen"))
206 Removed: (define phosphorus (get-nutrient #:name "Phosphorus"))
205 Added: (define examplium (get-nutrient #:name "Examplium"))
206 Added: (define ignorium (get-nutrient #:name "Ignorium"))
207 207
208 208 (create-fertilizer-product! canonical-product-name
209 Removed: "MasterBlend"
210 Removed: (hash nitrogen 40 phosphorus 200))
209 Added: "ExampleBrand"
210 Added: (hash examplium 40 ignorium 200))
211 211
212 212 (check-equal? (length (get-fertilizer-products)) 1)
213 213
214 214 (define fp (get-fertilizer-product #:canonical-name canonical-product-name))
215 215 (check-true (fertilizer-product? fp))
216 216 (check-equal? (fertilizer-product-canonical-name fp) canonical-product-name)
217 Removed: (check-equal? (fertilizer-product-brand-name fp) "MasterBlend"))
217 Added: (check-equal? (fertilizer-product-brand-name fp) "ExampleBrand"))
218 218
219 219 (test-case "Check all product values"
220 Removed: (define nitrogen (get-nutrient #:name "Nitrogen"))
221 Removed: (define phosphorus (get-nutrient #:name "Phosphorus"))
220 Added: (define examplium (get-nutrient #:name "Examplium"))
221 Added: (define ignorium (get-nutrient #:name "Ignorium"))
222 222
223 223 (define fp (get-fertilizer-product #:canonical-name canonical-product-name))
224 224
225 Removed: (check-= (get-fertilizer-product-value fp nitrogen) 40 0)
226 Removed: (check-= (get-fertilizer-product-value fp phosphorus) 200 0)
225 Added: (check-= (get-fertilizer-product-value fp examplium) 40 0)
226 Added: (check-= (get-fertilizer-product-value fp ignorium) 200 0)
227 227
228 228 (define fpv (fertilizer-product-nutrient-values fp))
229 229
@@ -240,13 +240,13 @@
240 240 (check-equal? fp fp-by-id))
241 241
242 242 (test-case "Handle missing nutrient in product"
243 Removed: (define potassium (get-nutrient #:name "Potassium"))
243 Added: (define testium (get-nutrient #:name "Testium"))
244 244 (define fp (get-fertilizer-product #:canonical-name canonical-product-name))
245 Removed: (check-false (get-fertilizer-product-value fp potassium)))
245 Added: (check-false (get-fertilizer-product-value fp testium)))
246 246
247 247 (test-case "Custom write property formatting"
248 Removed: (define nitrogen (get-nutrient #:name "Nitrogen"))
249 Removed: (define fp (create-fertilizer-product! "Test Fertilizer" "TestBrand" (hash nitrogen 50)))
248 Added: (define examplium (get-nutrient #:name "Examplium"))
249 Added: (define fp (create-fertilizer-product! "Test Fertilizer" "TestBrand" (hash examplium 50)))
250 250
251 251 (define output (open-output-string))
252 252 (write fp output)
@@ -255,7 +255,6 @@
255 255 (check-true (string-contains? result "Fertilizer #"))
256 256 (check-true (string-contains? result "Test Fertilizer"))
257 257 (check-true (string-contains? result "TestBrand")))
258 Removed:
259 258 (test-case "Delete product and cascade to product values"
260 259 (define fp (get-fertilizer-product #:canonical-name canonical-product-name))
261 260 (delete-fertilizer-product! fp)
models/nutrient-measurement.rkt
index 3a5238b9..7f954b69 100644..100644
@@ -221,29 +221,28 @@
221 221 (test-suite "Nutrient measurement model"
222 222 #:before (λ ()
223 223 (connect! #:path 'memory)
224 Removed: ;; (connect! #:path "test.sqlite3")
225 224 (migrate-all!)
226 Removed: (create-nutrient! "Nitrogen" "" "N")
227 Removed: (create-nutrient! "Phosphorus" "" "P")
228 Removed: (create-nutrient! "Potassium" "" "K"))
225 Added: (create-nutrient! "Examplium" "Examplium" "Ex")
226 Added: (create-nutrient! "Ignorium" "Ignorium" "Ig")
227 Added: (create-nutrient! "Testium" "Testium" "Ts"))
229 228 #:after (λ () (disconnect!))
230 229
231 230 (test-case "Create measurement with date and values"
232 Removed: (define nitrogen (get-nutrient #:name "Nitrogen"))
233 Removed: (define phosphorus (get-nutrient #:name "Phosphorus"))
234 Removed: (create-nutrient-measurement! measurement-date (hash nitrogen 12.3 phosphorus 4.5))
231 Added: (define examplium (get-nutrient #:name "Examplium"))
232 Added: (define ignorium (get-nutrient #:name "Ignorium"))
233 Added: (create-nutrient-measurement! measurement-date (hash examplium 12.3 ignorium 4.5))
235 234 (check-equal? (length (get-nutrient-measurements)) 1)
236 235 (define nm (get-nutrient-measurement #:date measurement-date))
237 236 (check-true (nutrient-measurement? nm))
238 237 (check-equal? (nutrient-measurement-measurement-date nm) measurement-date))
239 238
240 239 (test-case "Check all measurement values"
241 Removed: (define nitrogen (get-nutrient #:name "Nitrogen"))
242 Removed: (define phosphorus (get-nutrient #:name "Phosphorus"))
240 Added: (define examplium (get-nutrient #:name "Examplium"))
241 Added: (define ignorium (get-nutrient #:name "Ignorium"))
243 242
244 243 (define nm (get-nutrient-measurement #:date measurement-date))
245 Removed: (check-equal? (get-nutrient-measurement-value nm nitrogen) 12.3)
246 Removed: (check-equal? (get-nutrient-measurement-value nm phosphorus) 4.5)
244 Added: (check-equal? (get-nutrient-measurement-value nm examplium) 12.3)
245 Added: (check-equal? (get-nutrient-measurement-value nm ignorium) 4.5)
247 246
248 247 (define nmv (nutrient-measurement-nutrient-values nm))
249 248 (check-equal?
@@ -251,17 +250,17 @@
251 250 nmv
252 251 "return value of get-nutrient-measurement-values ≠ nutrient-measurement-values struct accessor")
253 252 (check-equal? (hash-count nmv) 2)
254 Removed: (check-equal? (hash-ref nmv nitrogen) 12.3)
255 Removed: (check-equal? (hash-ref nmv phosphorus) 4.5))
253 Added: (check-equal? (hash-ref nmv examplium) 12.3)
254 Added: (check-equal? (hash-ref nmv ignorium) 4.5))
256 255
257 256 (test-case "Retrieve latest measurement values"
258 Removed: (define nitrogen (get-nutrient #:name "Nitrogen"))
259 Removed: (define phosphorus (get-nutrient #:name "Phosphorus"))
257 Added: (define examplium (get-nutrient #:name "Examplium"))
258 Added: (define ignorium (get-nutrient #:name "Ignorium"))
260 259 (define second-measurement-date "2025-09-02")
261 Removed: (create-nutrient-measurement! second-measurement-date (hash nitrogen 6.7 phosphorus 8.9))
260 Added: (create-nutrient-measurement! second-measurement-date (hash examplium 6.7 ignorium 8.9))
262 261
263 Removed: (check-equal? (get-latest-nutrient-measurement-value nitrogen) 6.7)
264 Removed: (check-equal? (get-latest-nutrient-measurement-value phosphorus) 8.9))
262 Added: (check-equal? (get-latest-nutrient-measurement-value examplium) 6.7)
263 Added: (check-equal? (get-latest-nutrient-measurement-value ignorium) 8.9))
265 264
266 265 (test-case "Delete measurement and cascade to measurement values"
267 266 (define nm (get-nutrient-measurement #:date measurement-date))
models/nutrient-value.rkt
index 9653c7f7..4514d845 100644..100644
@@ -51,3 +51,153 @@
51 51 (for/hash ([r (in-list residuals)])
52 52 (match-define (vector n-id n-canonical-name n-french-name n-formula value-ppm) r)
53 53 (values (nutrient n-id n-canonical-name n-french-name n-formula) value-ppm)))
54 Added:
55 Added: (module+ test
56 Added: (require rackunit
57 Added: rackunit/text-ui
58 Added: db
59 Added: sql
60 Added: "../db/conn.rkt"
61 Added: "../db/migrations.rkt"
62 Added: "../models/nutrient.rkt")
63 Added:
64 Added: (run-tests (test-suite "Nutrient value model"
65 Added: #:before (λ ()
66 Added: (connect! #:path 'memory)
67 Added: (migrate-all!)
68 Added: (create-nutrient! "Examplium" "Examplium" "Ex")
69 Added: (create-nutrient! "Ignorium" "Ignorium" "Ig")
70 Added: (create-nutrient! "Testium" "Testium" "Ts")
71 Added: (create-nutrient! "Zeroium" "Zeroium" "Zr"))
72 Added: #:after (λ () (disconnect!))
73 Added:
74 Added: #;(test-case "Insert nutrient values"
75 Added: (define ex (get-nutrient #:name "Examplium"))
76 Added: (define ig (get-nutrient #:name "Ignorium"))
77 Added:
78 Added: ;; Create a nutrient_value_set for testing
79 Added: (define nvs-id
80 Added: (insert-id (query (current-conn)
81 Added: (insert #:into nutrient_value_sets
82 Added: #:set [nutrient_measurement_id ,sql-null]
83 Added: [crop_requirement_id ,sql-null]
84 Added: [fertilizer_product_id ,sql-null]))))
85 Added:
86 Added: (define nv-hash (hash ex 100.5 ig 50.25))
87 Added: (define result (insert-nutrient-values (current-conn) nvs-id nv-hash))
88 Added:
89 Added: (check-true (list? result))
90 Added: (check-true (assoc 'insert-id result))
91 Added:
92 Added: ;; Verify values were inserted
93 Added: (define ex-value
94 Added: (query-value (current-conn)
95 Added: (select value_ppm
96 Added: #:from nutrient_values
97 Added: #:where (and (= value_set_id ,nvs-id)
98 Added: (= nutrient_id ,(nutrient-id ex))))))
99 Added: (check-= ex-value 100.5 0.001))
100 Added:
101 Added: (test-case "Get sorted nutrient values filters positive and sorts descending"
102 Added: (define ex (get-nutrient #:name "Examplium"))
103 Added: (define ig (get-nutrient #:name "Ignorium"))
104 Added: (define ts (get-nutrient #:name "Testium"))
105 Added: (define zr (get-nutrient #:name "Zeroium"))
106 Added:
107 Added: (define nv-hash (hash ex 100 ig 50 ts 150 zr 0))
108 Added: (define sorted (get-sorted-nutrient-values nv-hash))
109 Added:
110 Added: ;; Should exclude zero values
111 Added: (check-equal? (length sorted) 3)
112 Added: ;; Should be sorted descending by value
113 Added: (check-equal? (cdr (first sorted)) 150) ; Testium
114 Added: (check-equal? (cdr (second sorted)) 100) ; Examplium
115 Added: (check-equal? (cdr (third sorted)) 50) ; Ignorium
116 Added: ;; Zeroium should not be in the list
117 Added: (check-false (member zr (map car sorted))))
118 Added:
119 Added: (test-case "Get sorted nutrient values with all zeros returns empty"
120 Added: (define ex (get-nutrient #:name "Examplium"))
121 Added: (define ig (get-nutrient #:name "Ignorium"))
122 Added:
123 Added: (define nv-hash (hash ex 0 ig 0))
124 Added: (define sorted (get-sorted-nutrient-values nv-hash))
125 Added:
126 Added: (check-equal? (length sorted) 0))
127 Added:
128 Added: (test-case "Get sorted nutrient values with negatives excluded"
129 Added: (define ex (get-nutrient #:name "Examplium"))
130 Added: (define ig (get-nutrient #:name "Ignorium"))
131 Added:
132 Added: ;; Note: nutrient-value? contract should prevent this, but testing the function
133 Added: (define nv-hash (hash ex 100 ig -50))
134 Added: (define sorted (get-sorted-nutrient-values nv-hash))
135 Added:
136 Added: (check-equal? (length sorted) 1)
137 Added: (check-equal? (cdr (first sorted)) 100))
138 Added:
139 Added: #;(test-case "Update nutrient values"
140 Added: (define ex (get-nutrient #:name "Examplium"))
141 Added: (define ig (get-nutrient #:name "Ignorium"))
142 Added:
143 Added: ;; Create initial values
144 Added: (define nvs-id
145 Added: (insert-id (query (current-conn)
146 Added: (insert #:into nutrient_value_sets
147 Added: #:set [nutrient_measurement_id ,sql-null]
148 Added: [crop_requirement_id ,sql-null]
149 Added: [fertilizer_product_id ,sql-null]))))
150 Added:
151 Added: (define initial-hash (hash ex 100 ig 50))
152 Added: (insert-nutrient-values (current-conn) nvs-id initial-hash)
153 Added:
154 Added: ;; Update values
155 Added: (define updated-hash (hash ex 200 ig 75))
156 Added: (update-nutrient-values! (current-conn) nvs-id updated-hash)
157 Added:
158 Added: ;; Verify updates
159 Added: (define ex-value
160 Added: (query-value (current-conn)
161 Added: (select value_ppm
162 Added: #:from nutrient_values
163 Added: #:where (and (= value_set_id ,nvs-id)
164 Added: (= nutrient_id ,(nutrient-id ex))))))
165 Added: (check-= ex-value 200 0.001)
166 Added:
167 Added: (define ig-value
168 Added: (query-value (current-conn)
169 Added: (select value_ppm
170 Added: #:from nutrient_values
171 Added: #:where (and (= value_set_id ,nvs-id)
172 Added: (= nutrient_id ,(nutrient-id ig))))))
173 Added: (check-= ig-value 75 0.001))
174 Added:
175 Added: (test-case "Residuals to nutrient value hash conversion"
176 Added: (define ex (get-nutrient #:name "Examplium"))
177 Added: (define ig (get-nutrient #:name "Ignorium"))
178 Added:
179 Added: (define residuals
180 Added: (list (vector (nutrient-id ex) "Examplium" "Examplium" "Ex" 123.45)
181 Added: (vector (nutrient-id ig) "Ignorium" "Ignorium" "Ig" 67.89)))
182 Added:
183 Added: (define nv-hash (residuals->nutrient-value-hash residuals))
184 Added:
185 Added: (check-equal? (hash-count nv-hash) 2)
186 Added: (check-= (hash-ref nv-hash ex) 123.45 0.001)
187 Added: (check-= (hash-ref nv-hash ig) 67.89 0.001))
188 Added:
189 Added: (test-case "Residuals to nutrient value hash with empty list"
190 Added: (define nv-hash (residuals->nutrient-value-hash '()))
191 Added: (check-true (hash-empty? nv-hash)))
192 Added:
193 Added: (test-case "Residuals to nutrient value hash preserves all fields"
194 Added: (define residuals (list (vector 1 "Examplium" "Examplium-FR" "Ex" 100.0)))
195 Added:
196 Added: (define nv-hash (residuals->nutrient-value-hash residuals))
197 Added: (define nutrient-key (car (hash-keys nv-hash)))
198 Added:
199 Added: (check-equal? (nutrient-id nutrient-key) 1)
200 Added: (check-equal? (nutrient-canonical-name nutrient-key) "Examplium")
201 Added: (check-equal? (nutrient-french-name nutrient-key) "Examplium-FR")
202 Added: (check-equal? (nutrient-formula nutrient-key) "Ex")
203 Added: (check-= (hash-ref nv-hash nutrient-key) 100.0 0.001)))))