(ns auto-ap.auth.jwt-test (:require [auto-ap.integration.util :refer [wrap-setup]] [auto-ap.routes.auth :as auth] [buddy.sign.jwt :as jwt] [clj-time.coerce :as coerce] [clj-time.core :as time] [clojure.test :refer [deftest is testing use-fixtures]] [config.core :refer [env]])) (use-fixtures :each wrap-setup) (deftest test-user->jwt-generates-token (testing "Behavior 7.1: It should generate a JWT containing the user's role and client access on login" (let [user {:db/id 123 :user/name "Test User" :user/role :user-role/user :user/clients [{:db/id 1 :client/code "A" :client/locations ["DT"]} {:db/id 2 :client/code "B" :client/locations ["MH"]}]} token (auth/user->jwt user "fake-oauth-token")] (is (= "Test User" (:user token))) (is (= "user" (:user/role token))) (is (= 123 (:db/id token))) (is (= "Test User" (:user/name token))) (is (some? (:exp token)))))) (deftest test-admin-jwt-compresses-clients (testing "Behavior 7.2: It should compress the client list for admin users to fit in the JWT" (let [user {:db/id 1 :user/name "Admin" :user/role :user-role/admin :user/clients [{:db/id 10 :client/code "A" :client/locations ["DT"]} {:db/id 20 :client/code "B" :client/locations ["MH"]}]} token (auth/user->jwt user "fake-oauth-token")] (is (= "admin" (:user/role token))) (is (some? (:gz-clients token))) (is (string? (:gz-clients token))) (is (nil? (:user/clients token))) ;; Verify the compressed data can be decompressed (let [decompressed (auth/gunzip (:gz-clients token))] (is (= [{:db/id 10 :client/code "A" :client/locations ["DT"]} {:db/id 20 :client/code "B" :client/locations ["MH"]}] decompressed)))))) (deftest test-readonly-jwt-compresses-clients (testing "Behavior 7.3: It should compress the client list for read-only users to fit in the JWT" (let [user {:db/id 2 :user/name "Read Only" :user/role :user-role/read-only :user/clients [{:db/id 30 :client/code "C" :client/locations ["DT"]}]} token (auth/user->jwt user "fake-oauth-token")] (is (= "read-only" (:user/role token))) (is (some? (:gz-clients token))) (is (string? (:gz-clients token))) (is (nil? (:user/clients token))) (let [decompressed (auth/gunzip (:gz-clients token))] (is (= [{:db/id 30 :client/code "C" :client/locations ["DT"]}] decompressed)))))) (deftest test-regular-user-jwt-plain-clients (testing "Behavior 7.4: It should include a plain client list for regular users in the JWT" (let [user {:db/id 3 :user/name "Regular" :user/role :user-role/user :user/clients [{:db/id 40 :client/code "D" :client/locations ["DT"]}]} token (auth/user->jwt user "fake-oauth-token")] (is (= "user" (:user/role token))) (is (some? (:user/clients token))) (is (sequential? (:user/clients token))) (is (nil? (:gz-clients token))) (is (= [{:db/id 40 :client/code "D" :client/locations ["DT"]}] (:user/clients token)))))) (deftest test-api-token (testing "Behavior 7.5: It should create API tokens with admin role and 1000-day expiration" (let [token-str (auth/make-api-token) claims (jwt/unsign token-str (:jwt-secret env) {:alg :hs512}) exp-dt (coerce/from-long (* 1000 (long (:exp claims)))) now (time/now)] (is (= "API" (:user claims))) (is (= "admin" (:user/role claims))) (is (= "API" (:user/name claims))) (is (some? (:exp claims))) ;; Verify expiration is approximately 1000 days from now (is (time/after? exp-dt now)) (is (time/before? exp-dt (time/plus now (time/days 1001))))))) (deftest test-gzip-roundtrip (testing "gzip and gunzip are inverse operations" (let [data [{:db/id 1 :client/code "A"} {:db/id 2 :client/code "B"}] compressed (auth/gzip data) decompressed (auth/gunzip compressed)] (is (= data decompressed)))))