From e8e451054df640395607fb27d10dbb4c84f6c68b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 29 May 2026 14:32:16 +0000 Subject: [PATCH] Implement flash message core. --- flash/package.lisp | 30 ++++++++++++++++++ io.github.cl-sdk.wst.flash.asd | 8 +++++ io.github.cl-sdk.wst.flash.test.asd | 10 ++++++ io.github.cl-sdk.wst.test.asd | 1 + readme.md | 1 + t/flash-tests.lisp | 49 +++++++++++++++++++++++++++++ 6 files changed, 99 insertions(+) create mode 100644 flash/package.lisp create mode 100644 io.github.cl-sdk.wst.flash.asd create mode 100644 io.github.cl-sdk.wst.flash.test.asd create mode 100644 t/flash-tests.lisp diff --git a/flash/package.lisp b/flash/package.lisp new file mode 100644 index 0000000..5a48111 --- /dev/null +++ b/flash/package.lisp @@ -0,0 +1,30 @@ +(defpackage #:io.github.cl-sdk.wst.flash + (:use #:cl) + (:export + #:flash-messages + #:append-flash-message + #:consume-flash-messages + #:clear-flash-messages + #:valid-flash-type-p)) + +(in-package #:io.github.cl-sdk.wst.flash) + +(defgeneric flash-messages (session &key &allow-other-keys) + (:documentation "Return queued flash messages for SESSION-ID without consuming them. + +Always returns a list (possibly empty).")) + +(defgeneric append-flash-message (session text &key &allow-other-keys) + (:documentation "Append a normalized flash message for SESSION and return that message. + +Signals an error for invalid payload/type or queue overflow.")) + +(defgeneric consume-flash-messages (session &key &allow-other-keys) + (:documentation "Return and clear queued flash messages for SESSION. + +Always returns a list (possibly empty).")) + +(defgeneric clear-flash-messages (session &key &allow-other-keys) + (:documentation "Clear queued flash messages for SESSION. + +Returns T when a queue was removed, NIL otherwise.")) diff --git a/io.github.cl-sdk.wst.flash.asd b/io.github.cl-sdk.wst.flash.asd new file mode 100644 index 0000000..eeefc4d --- /dev/null +++ b/io.github.cl-sdk.wst.flash.asd @@ -0,0 +1,8 @@ +(asdf:defsystem #:io.github.cl-sdk.wst.flash + :description "Flash message API primitives for wst." + :author "Bruno Dias" + :license "Unlicense" + :version "0.1.0" + :serial t + :pathname "flash" + :components ((:file "package"))) diff --git a/io.github.cl-sdk.wst.flash.test.asd b/io.github.cl-sdk.wst.flash.test.asd new file mode 100644 index 0000000..e6a7250 --- /dev/null +++ b/io.github.cl-sdk.wst.flash.test.asd @@ -0,0 +1,10 @@ +(asdf:defsystem #:io.github.cl-sdk.wst.flash.test + :description "Tests for io.github.cl-sdk.wst.flash." + :author "Bruno Dias" + :license "Unlicense" + :version "0.1.0" + :depends-on (#:io.github.cl-sdk.wst.test.support + #:io.github.cl-sdk.wst.flash) + :pathname "t" + :serial t + :components ((:file "flash-tests"))) diff --git a/io.github.cl-sdk.wst.test.asd b/io.github.cl-sdk.wst.test.asd index 8b52b93..6a42c7d 100644 --- a/io.github.cl-sdk.wst.test.asd +++ b/io.github.cl-sdk.wst.test.asd @@ -5,6 +5,7 @@ :version "0.1.0" :depends-on (#:io.github.cl-sdk.wst.circuit-breaker.test #:io.github.cl-sdk.wst.cors.test + #:io.github.cl-sdk.wst.flash.test #:io.github.cl-sdk.wst.feature-flag.routing.test #:io.github.cl-sdk.wst.feature-flag.test #:io.github.cl-sdk.wst.idempotency.test diff --git a/readme.md b/readme.md index 954965c..c2cbd4a 100644 --- a/readme.md +++ b/readme.md @@ -15,6 +15,7 @@ Behavior is built from pipelines and composition, where middleware and handlers - HTTP request/response helpers - Cookie parsing utilities - Session and CSRF support +- Session-backed one-time flash messages - Request content parsing middleware - CORS middleware - Static file routing diff --git a/t/flash-tests.lisp b/t/flash-tests.lisp new file mode 100644 index 0000000..44e8403 --- /dev/null +++ b/t/flash-tests.lisp @@ -0,0 +1,49 @@ +(defpackage #:io.github.cl-sdk.wst.flash.test + (:use #:cl #:fiveam #:io.github.cl-sdk.wst.flash)) + +(in-package #:io.github.cl-sdk.wst.flash.test) + +(def-suite flash-suite) +(in-suite flash-suite) + +(defclass test-memory-session-object () + ((data :initarg :data :initform nil :accessor test-memory-object-data))) + +(defmethod io.github.cl-sdk.wst.flash:flash-messages ((o test-memory-session-object) &key) + (getf (slot-value o 'data) :flash-messages)) + +(defmethod io.github.cl-sdk.wst.flash:append-flash-message ((o test-memory-session-object) text &key) + (let ((messages (getf (slot-value o 'data) :flash-messages))) + (setf (getf (slot-value o 'data) :flash-messages) + (append messages (list text))) + o)) + +(defmethod io.github.cl-sdk.wst.flash:consume-flash-messages ((o test-memory-session-object) &key) + (pop (getf (slot-value o 'data) :flash-messages))) + +(test must-append-flash-message + (let ((session-object (make-instance 'test-memory-session-object))) + (is (equal '() (flash-messages session-object))) + (append-flash-message session-object "created") + (let ((messages (flash-messages session-object))) + (is (= 1 (length messages))) + (is (equal "created" (first messages)))) + (let ((message (consume-flash-messages session-object)) + (messages (flash-messages session-object))) + (is (= 0 (length messages))) + (is (equal "created" message))))) + +(test must-read-flash-messages-without-cosuming-it + (let ((session-object (make-instance 'test-memory-session-object))) + (append-flash-message session-object "created") + (let ((messages (flash-messages session-object))) + (is (= 1 (length messages))) + (is (equal "created" (first messages)))))) + +(test must-read-flash-messages-cosuming-the-first + (let ((session-object (make-instance 'test-memory-session-object))) + (append-flash-message session-object "created") + (let ((message (consume-flash-messages session-object)) + (messages (flash-messages session-object))) + (is (= 0 (length messages))) + (is (equal "created" message)))))