From 92b9c68c5dfb3c28e3d03a29547d6174b8b72983 Mon Sep 17 00:00:00 2001 From: PeanutPiglet Date: Thu, 17 Sep 2026 18:14:56 -0400 Subject: [PATCH 1/4] add test case for graph controller --- README.md | 1 + backend-test/Controllers/GraphControllerTests.hs | 10 +++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9aef5c4ed..4a4ae9775 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,7 @@ Sam Shaftoe, Ian Stewart-Binks, Alan Su, Maryam Taj, +Jack Tang, Betty Wang, Rui Weng, Fullchee Zhang, diff --git a/backend-test/Controllers/GraphControllerTests.hs b/backend-test/Controllers/GraphControllerTests.hs index b960e0064..39427d0f3 100644 --- a/backend-test/Controllers/GraphControllerTests.hs +++ b/backend-test/Controllers/GraphControllerTests.hs @@ -7,12 +7,14 @@ module Controllers.GraphControllerTests ( ) where import Config (runDb) +import Control.Monad (when) import Control.Monad.IO.Class (liftIO) import Controllers.Graph (getGraphJSON, index, saveGraphJSON) import Data.Aeson (Value (Number, Object), decode) import qualified Data.Aeson.Key as Key import qualified Data.Aeson.KeyMap as KeyMap import qualified Data.ByteString.Lazy.Char8 as BL +import Data.Maybe (isNothing) import qualified Data.Text as T import Database.DataType (ShapeType (..)) import Database.Persist.Sqlite (SqlPersistM, insert_, toSqlKey) @@ -87,6 +89,10 @@ saveGraphJSONTestCases = ( "Multi-node graph" , "{\"texts\":[{\"graph\":1,\"rId\":\"t1\",\"pos\":[10.0,10.0],\"text\":\"Graph text 1\",\"align\":\"left\",\"fill\":\"black\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]},{\"graph\":1,\"rId\":\"t2\",\"pos\":[20.0,20.0],\"text\":\"Graph text 2\",\"align\":\"center\",\"fill\":\"blue\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]}],\"shapes\":[{\"graph\":1,\"id_\":\"s1\",\"pos\":[100.0,100.0],\"width\":100.0,\"height\":50.0,\"fill\":\"white\",\"stroke\":\"black\",\"text\":[],\"type_\":\"Node\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]},{\"graph\":1,\"id_\":\"h2\",\"pos\":[200.0,200.0],\"width\":50.0,\"height\":10.0,\"fill\":\"green\",\"stroke\":\"blue\",\"text\":[],\"type_\":\"Hybrid\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]},{\"graph\":1,\"id_\":\"s3\",\"pos\":[300.0,300.0],\"width\":30.0,\"height\":30.0,\"fill\":\"red\",\"stroke\":\"purple\",\"text\":[],\"type_\":\"BoolNode\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]}],\"paths\":[{\"graph\":1,\"id_\":\"p1\",\"points\":[[50.0,50.0],[150.0,50.0]],\"fill\":\"white\",\"stroke\":\"black\",\"isRegion\":false,\"source\":\"s1\",\"target\":\"h2\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]},{\"graph\":1,\"id_\":\"p2\",\"points\":[[100.0,20.0],[30.0,40.0]],\"fill\":\"yellow\",\"stroke\":\"orange\",\"isRegion\":false,\"source\":\"h2\",\"target\":\"s3\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]}]}" ) + , + ( "Invalid graph" + , "not valid JSON" + ) ] -- | Run a test case (case, graph JSON payload) @@ -95,12 +101,14 @@ runSaveGraphJSONTest (label, payload) = testCase label $ do runDb clearDatabase let graphName = "Test Graph Name" - _ <- + response <- runServerPartWith Controllers.Graph.saveGraphJSON $ mockPutRequest "/graph-save" [("nameData", T.unpack graphName), ("jsonData", BL.unpack payload)] "" retrievedResult <- liftIO $ Models.Graph.getGraph graphName let expectedValue = fmap addDefaults (decode payload :: Maybe Value) assertEqual ("Unexpected response for " ++ label) expectedValue retrievedResult + when (isNothing expectedValue) $ + assertEqual "Unexpected response for invalid JSON" "Error" (BL.unpack $ rsBody response) -- | Run all save graph test cases runSaveGraphJSONTests :: [TestTree] From 72bdbb8068f1dc42d0c33762919cfe09f2db3fa0 Mon Sep 17 00:00:00 2001 From: PeanutPiglet Date: Thu, 17 Sep 2026 18:43:10 -0400 Subject: [PATCH 2/4] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05f432b96..a9fa85de8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Switched CI provider from CircleCI to GitHub Actions - Cleared up documentation for various graph-related front-end functions - Adopted TypeScript v7 into build and CI pipelines and converted `js/components/graph/Button.js` to `Button.tsx` as a proof of concept +- Added test case coverage for `app/Controllers/Graph.hs` ## [0.8.1] - 2026-08-10 From 4bbc202fd4907b74c79ed26434b28966a632de73 Mon Sep 17 00:00:00 2001 From: PeanutPiglet Date: Sun, 20 Sep 2026 11:52:57 -0400 Subject: [PATCH 3/4] Refactored graph controller save graph invalid test --- .../Controllers/GraphControllerTests.hs | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/backend-test/Controllers/GraphControllerTests.hs b/backend-test/Controllers/GraphControllerTests.hs index 39427d0f3..954cf5f0c 100644 --- a/backend-test/Controllers/GraphControllerTests.hs +++ b/backend-test/Controllers/GraphControllerTests.hs @@ -7,14 +7,12 @@ module Controllers.GraphControllerTests ( ) where import Config (runDb) -import Control.Monad (when) import Control.Monad.IO.Class (liftIO) import Controllers.Graph (getGraphJSON, index, saveGraphJSON) import Data.Aeson (Value (Number, Object), decode) import qualified Data.Aeson.Key as Key import qualified Data.Aeson.KeyMap as KeyMap import qualified Data.ByteString.Lazy.Char8 as BL -import Data.Maybe (isNothing) import qualified Data.Text as T import Database.DataType (ShapeType (..)) import Database.Persist.Sqlite (SqlPersistM, insert_, toSqlKey) @@ -89,10 +87,6 @@ saveGraphJSONTestCases = ( "Multi-node graph" , "{\"texts\":[{\"graph\":1,\"rId\":\"t1\",\"pos\":[10.0,10.0],\"text\":\"Graph text 1\",\"align\":\"left\",\"fill\":\"black\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]},{\"graph\":1,\"rId\":\"t2\",\"pos\":[20.0,20.0],\"text\":\"Graph text 2\",\"align\":\"center\",\"fill\":\"blue\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]}],\"shapes\":[{\"graph\":1,\"id_\":\"s1\",\"pos\":[100.0,100.0],\"width\":100.0,\"height\":50.0,\"fill\":\"white\",\"stroke\":\"black\",\"text\":[],\"type_\":\"Node\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]},{\"graph\":1,\"id_\":\"h2\",\"pos\":[200.0,200.0],\"width\":50.0,\"height\":10.0,\"fill\":\"green\",\"stroke\":\"blue\",\"text\":[],\"type_\":\"Hybrid\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]},{\"graph\":1,\"id_\":\"s3\",\"pos\":[300.0,300.0],\"width\":30.0,\"height\":30.0,\"fill\":\"red\",\"stroke\":\"purple\",\"text\":[],\"type_\":\"BoolNode\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]}],\"paths\":[{\"graph\":1,\"id_\":\"p1\",\"points\":[[50.0,50.0],[150.0,50.0]],\"fill\":\"white\",\"stroke\":\"black\",\"isRegion\":false,\"source\":\"s1\",\"target\":\"h2\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]},{\"graph\":1,\"id_\":\"p2\",\"points\":[[100.0,20.0],[30.0,40.0]],\"fill\":\"yellow\",\"stroke\":\"orange\",\"isRegion\":false,\"source\":\"h2\",\"target\":\"s3\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]}]}" ) - , - ( "Invalid graph" - , "not valid JSON" - ) ] -- | Run a test case (case, graph JSON payload) @@ -101,19 +95,26 @@ runSaveGraphJSONTest (label, payload) = testCase label $ do runDb clearDatabase let graphName = "Test Graph Name" - response <- + _ <- runServerPartWith Controllers.Graph.saveGraphJSON $ mockPutRequest "/graph-save" [("nameData", T.unpack graphName), ("jsonData", BL.unpack payload)] "" retrievedResult <- liftIO $ Models.Graph.getGraph graphName let expectedValue = fmap addDefaults (decode payload :: Maybe Value) assertEqual ("Unexpected response for " ++ label) expectedValue retrievedResult - when (isNothing expectedValue) $ - assertEqual "Unexpected response for invalid JSON" "Error" (BL.unpack $ rsBody response) --- | Run all save graph test cases +-- | Run all save graph test cases on valid inputs runSaveGraphJSONTests :: [TestTree] runSaveGraphJSONTests = map runSaveGraphJSONTest saveGraphJSONTestCases +-- | Run save graph test on invalid input +runSaveGraphJSONInvalidJSONTest :: TestTree +runSaveGraphJSONInvalidJSONTest = testCase "Invalid JSON graph" $ do + runDb clearDatabase + response <- + runServerPartWith Controllers.Graph.saveGraphJSON $ + mockPutRequest "/graph-save" [("nameData", "Invalid Graph Name"), ("jsonData", "Invalid JSON")] "" + assertEqual "Unexpected response for invalid JSON" "Error" (BL.unpack $ rsBody response) + -- | List of test cases for getGraphJSON as (label, (texts, shapes, paths)) -- | Invariant: Expected Graph IDs are all set to 1 getGraphJSONTestCases :: [(String, ([Text], [Shape], [Path]))] @@ -221,4 +222,7 @@ runGetGraphJSONTests = map runGetGraphJSONTest getGraphJSONTestCases -- | Test suite for Graph Controller Module test_graphController :: TestTree -test_graphController = withDatabase "Graph Controller tests" (runIndexTests ++ runSaveGraphJSONTests ++ runGetGraphJSONTests) +test_graphController = + withDatabase + "Graph Controller tests" + (runIndexTests ++ runSaveGraphJSONTests ++ [runSaveGraphJSONInvalidJSONTest] ++ runGetGraphJSONTests) From ace30f42f9b7a91cf539fd532bf342a0bca561df Mon Sep 17 00:00:00 2001 From: PeanutPiglet Date: Sun, 20 Sep 2026 12:03:30 -0400 Subject: [PATCH 4/4] Updated changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d0dfa517..ddebbd62d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Cleared up documentation for various graph-related front-end functions - Adopted TypeScript v7 into build and CI pipelines and converted `js/components/graph/Button.js` to `Button.tsx` as a proof of concept - Added `.gitattributes` file to standardize to LF line endings and renormalized codebase +- Added invalid JSON graph test coverage for `saveGraphJSON` in `app/Controllers/Graph.hs` ## [0.8.1] - 2026-08-10