summaryrefslogtreecommitdiff
path: root/models/crop-rotation.rkt
diff options
context:
space:
mode:
Diffstat (limited to 'models/crop-rotation.rkt')
-rw-r--r--models/crop-rotation.rkt158
1 files changed, 158 insertions, 0 deletions
diff --git a/models/crop-rotation.rkt b/models/crop-rotation.rkt
index 3b0a69d..fb7775f 100644
--- a/models/crop-rotation.rkt
+++ b/models/crop-rotation.rkt
@@ -3,6 +3,7 @@
(provide crop-rotation
crop-rotation?
crop-rotation-id
+ crop-rotation-requirement
(rename-out [crop-rotation-rotation-date crop-rotation-date]
[crop-rotation-requirement-proportions crop-rotation-requirements])
(contract-out
@@ -20,6 +21,9 @@
(struct crop-rotation (id rotation-date requirement-proportions) #:transparent)
+(define (crop-rotation-requirement cr requirement)
+ (hash-ref (crop-rotation-requirement-proportions cr) requirement #f))
+
(define crop-rotation-or-id/c (or/c crop-rotation? db-id?))
(define requirement-proportion-hash/c (hash/c crop-requirement? (between/c 0 100) #:immutable #t))
@@ -112,3 +116,157 @@
(define (delete-crop-rotation! cr-or-id)
(query-exec (current-conn) (delete #:from crop_rotations #:where (= id ,(->cr-id cr-or-id)))))
+
+(module+ test
+ (require rackunit
+ rackunit/text-ui
+ "../db/conn.rkt"
+ "../db/migrations.rkt"
+ "../models/nutrient.rkt"
+ "../models/crop.rkt"
+ "../models/crop-requirement.rkt")
+
+ (define rotation-date-1 "2025-01-15")
+ (define rotation-date-2 "2025-02-20")
+
+ (run-tests
+ (test-suite "Crop rotation model"
+ #:before (λ ()
+ (connect! #:path 'memory)
+ (migrate-all!)
+ (create-nutrient! "Examplium" "Examplium" "Ex")
+ (create-nutrient! "Ignorium" "Ignorium" "Ig")
+ (create-nutrient! "Testium" "Testium" "Ts"))
+ #:after (λ () (disconnect!))
+
+ (test-case "Create crop rotation with requirement proportions"
+ (define ex (get-nutrient #:name "Examplium"))
+ (define ig (get-nutrient #:name "Ignorium"))
+
+ (define req1 (create-crop-requirement! "vegetative" (hash ex 100 ig 50)))
+ (define req2 (create-crop-requirement! "fruiting" (hash ex 150 ig 75)))
+
+ (define proportions (hash req1 60 req2 40))
+ (define rotation (create-crop-rotation! rotation-date-1 proportions))
+
+ (check-true (crop-rotation? rotation))
+ (check-equal? (crop-rotation-rotation-date rotation) rotation-date-1)
+ (check-equal? (hash-count (crop-rotation-requirement-proportions rotation)) 2)
+
+ ;; Find requirements in the returned hash by profile
+ (define reqs (crop-rotation-requirement-proportions rotation))
+ (define req1-found
+ (findf (λ (r) (equal? (crop-requirement-profile r) "vegetative")) (hash-keys reqs)))
+ (define req2-found
+ (findf (λ (r) (equal? (crop-requirement-profile r) "fruiting")) (hash-keys reqs)))
+
+ (check-equal? (hash-ref reqs req1-found) 60)
+ (check-equal? (hash-ref reqs req2-found) 40))
+
+ (test-case "Create duplicate rotation returns existing"
+ ;; Get all requirements to find the ones we need
+ (define all-reqs (get-crop-requirements))
+ (define req1 (findf (λ (r) (equal? (crop-requirement-profile r) "vegetative")) all-reqs))
+ (define req2 (findf (λ (r) (equal? (crop-requirement-profile r) "fruiting")) all-reqs))
+
+ ;; Try to create with different proportions - should return existing
+ (define proportions (hash req1 70 req2 30))
+ (define rotation2 (create-crop-rotation! rotation-date-1 proportions))
+
+ (check-equal? (length (get-crop-rotations)) 1)
+
+ ;; The returned rotation should have original proportions (60/40), not new ones (70/30)
+ (define reqs2 (crop-rotation-requirement-proportions rotation2))
+ (define req1-from-rotation
+ (findf (λ (r) (equal? (crop-requirement-profile r) "vegetative")) (hash-keys reqs2)))
+ (define req2-from-rotation
+ (findf (λ (r) (equal? (crop-requirement-profile r) "fruiting")) (hash-keys reqs2)))
+
+ (check-equal? (hash-ref reqs2 req1-from-rotation) 60)
+ (check-equal? (hash-ref reqs2 req2-from-rotation) 40))
+
+ (test-case "Get crop rotation by id"
+ (define rotation1 (get-crop-rotation #:date rotation-date-1))
+ (define rotation2 (get-crop-rotation #:id (crop-rotation-id rotation1)))
+ (check-equal? (crop-rotation-id rotation1) (crop-rotation-id rotation2))
+ (check-equal? (crop-rotation-rotation-date rotation1) (crop-rotation-rotation-date rotation2)))
+
+ (test-case "Get crop rotation by date"
+ (define rotation (get-crop-rotation #:date rotation-date-1))
+ (check-true (crop-rotation? rotation))
+ (check-equal? (crop-rotation-rotation-date rotation) rotation-date-1))
+
+ (test-case "Get crop rotation with both id and date"
+ (define rotation1 (get-crop-rotation #:date rotation-date-1))
+ (define rotation2 (get-crop-rotation #:id (crop-rotation-id rotation1) #:date rotation-date-1))
+ (check-equal? (crop-rotation-id rotation1) (crop-rotation-id rotation2)))
+
+ (test-case "Get non-existent crop rotation"
+ (check-false (get-crop-rotation #:date "2099-12-31"))
+ (check-false (get-crop-rotation #:id 9999)))
+
+ (test-case "Get all crop rotations ordered by date descending"
+ (define ts (get-nutrient #:name "Testium"))
+ (define req3 (create-crop-requirement! "maintenance" (hash ts 80)))
+ (create-crop-rotation! rotation-date-2 (hash req3 100))
+
+ (define rotations (get-crop-rotations))
+ (check-equal? (length rotations) 2)
+ ;; Most recent first
+ (check-equal? (crop-rotation-rotation-date (first rotations)) rotation-date-2)
+ (check-equal? (crop-rotation-rotation-date (second rotations)) rotation-date-1))
+
+ (test-case "Get latest crop rotation"
+ (define latest (get-latest-crop-rotation))
+ (check-true (crop-rotation? latest))
+ (check-equal? (crop-rotation-rotation-date latest) rotation-date-2))
+
+ (test-case "Get latest returns #f when no rotations exist"
+ (for ([rotation (get-crop-rotations)])
+ (delete-crop-rotation! (crop-rotation-id rotation)))
+ (check-false (get-latest-crop-rotation)))
+
+ (test-case "Delete crop rotation by struct"
+ (define ts (get-nutrient #:name "Testium"))
+ (define req
+ (findf (λ (r) (equal? (crop-requirement-profile r) "maintenance")) (get-crop-requirements)))
+ (create-crop-rotation! "2025-03-01" (hash req 100))
+
+ (define rotation (get-crop-rotation #:date "2025-03-01"))
+ (delete-crop-rotation! rotation)
+ (check-false (get-crop-rotation #:id (crop-rotation-id rotation))))
+
+ (test-case "Delete crop rotation by id"
+ (define ts (get-nutrient #:name "Testium"))
+ (define req
+ (findf (λ (r) (equal? (crop-requirement-profile r) "maintenance")) (get-crop-requirements)))
+ (create-crop-rotation! "2025-04-01" (hash req 100))
+
+ (define rotation (get-crop-rotation #:date "2025-04-01"))
+ (define rotation-id-val (crop-rotation-id rotation))
+ (delete-crop-rotation! rotation-id-val)
+ (check-false (get-crop-rotation #:id rotation-id-val)))
+
+ (test-case "Delete crop rotation cascades to crop_rotation_requirements"
+ (define ex (get-nutrient #:name "Examplium"))
+ (define req1 (create-crop-requirement! "test-profile-1" (hash ex 100)))
+ (define req2 (create-crop-requirement! "test-profile-2" (hash ex 200)))
+ (create-crop-rotation! "2025-05-01" (hash req1 50 req2 50))
+
+ (define initial-count (length (get-crop-rotations)))
+ (define rotation (get-crop-rotation #:date "2025-05-01"))
+ (delete-crop-rotation! rotation)
+ (check-equal? (length (get-crop-rotations)) (- initial-count 1)))
+
+ (test-case "Rotation with associated crop requirement"
+ (define ex (get-nutrient #:name "Examplium"))
+ (define test-crop (create-crop! (crop #f "test-crop-for-rotation")))
+ (define req-with-crop (create-crop-requirement! "crop-specific" (hash ex 120) test-crop))
+ (define rotation (create-crop-rotation! "2025-06-01" (hash req-with-crop 100)))
+
+ (check-equal? (crop-requirement-crop-id req-with-crop) (crop-id test-crop))
+ (define retrieved (get-crop-rotation #:date "2025-06-01"))
+ (check-equal? (hash-count (crop-rotation-requirement-proportions retrieved)) 1))
+
+ (test-case "Error when neither id nor date provided"
+ (check-exn exn:fail? (λ () (get-crop-rotation)))))))
Copyright 2019--2026 Marius PETER