From 7335f9d58d504f9600d327a724ecca5fe3e8eb0b Mon Sep 17 00:00:00 2001 From: Alan Su Date: Fri, 16 Jan 2026 00:21:21 -0500 Subject: [PATCH] Add tests for Controllers.Graph saveGraphJSON function --- CHANGELOG.md | 1 + README.md | 1 + .../Controllers/GraphControllerTests.hs | 43 +++++++++++++++++-- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 93b83503d..bcb1cde5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### 🔧 Internal changes - Refactor GraphDropdown component from being a child of Graph to being a child of NavBar +- Added test cases for the saveGraphJSON function in `Controllers/Graph` ## [0.7.2] - 2025-12-10 diff --git a/README.md b/README.md index 29318a9f0..f122193c1 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ Harsh Patel, Eleonora Scognamiglio, Sam Shaftoe, Ian Stewart-Binks, +Alan Su, Maryam Taj, Betty Wang, Fullchee Zhang, diff --git a/backend-test/Controllers/GraphControllerTests.hs b/backend-test/Controllers/GraphControllerTests.hs index cd9b7dcb3..1f1378bcd 100644 --- a/backend-test/Controllers/GraphControllerTests.hs +++ b/backend-test/Controllers/GraphControllerTests.hs @@ -10,15 +10,18 @@ module Controllers.GraphControllerTests ) where import Config (runDb) -import Controllers.Graph (index) +import Control.Monad.IO.Class (liftIO) +import Controllers.Graph (index, saveGraphJSON) +import Data.Aeson (Value, decode) import qualified Data.ByteString.Lazy.Char8 as BL import qualified Data.Text as T import Database.Persist.Sqlite (SqlPersistM, insert_) import Database.Tables (Graph (..)) import Happstack.Server (rsBody) +import Models.Graph (getGraph) import Test.Tasty (TestTree) import Test.Tasty.HUnit (assertEqual, testCase) -import TestHelpers (clearDatabase, runServerPart, withDatabase) +import TestHelpers (clearDatabase, mockPutRequest, runServerPart, runServerPartWith, withDatabase) -- | List of test cases as (label, input graphs, expected output) indexTestCases :: [(String, [T.Text], String)] @@ -59,6 +62,40 @@ runIndexTest label graphs expected = runIndexTests :: [TestTree] runIndexTests = map (\(label, graphs, expected) -> runIndexTest label graphs expected) indexTestCases +-- | List of test cases as (label, input JSON payload, expected JSON payload) +saveGraphJSONTestCases :: [(String, BL.ByteString, BL.ByteString)] +saveGraphJSONTestCases = + [ ("Empty graph", + "{\"texts\":[],\"shapes\":[],\"paths\":[]}", + "{\"texts\":[],\"shapes\":[],\"paths\":[],\"width\":256,\"height\":256}" + ), + + ("Single node graph", + "{\"texts\":[],\"shapes\":[{\"graph\": 1, \"id_\": \"s1\", \"pos\": [100.0, 100.0], \"width\": 50.0, \"height\": 50.0, \"fill\": \"white\", \"stroke\": \"black\", \"text\": [], \"type_\": \"Node\", \"transform\": [1.0, 0.0, 0.0, 1.0, 0.0, 0.0]}],\"paths\":[]}", + "{\"texts\":[],\"shapes\":[{\"graph\": 1, \"id_\": \"s1\", \"pos\": [100.0, 100.0], \"width\": 50.0, \"height\": 50.0, \"fill\": \"white\", \"stroke\": \"black\", \"text\": [], \"type_\": \"Node\", \"transform\": [1.0, 0.0, 0.0, 1.0, 0.0, 0.0]}],\"paths\":[],\"width\":256,\"height\":256}" + ), + + ("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\":[100.0,100.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\":true,\"source\":\"h2\",\"target\":\"s3\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]}]}", + "{\"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\":[100.0,100.0],\"text\":\"Graph text 2\",\"align\":\"center\",\"fill\":\"blue\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]},{\"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]}],\"shapes\":[{\"graph\":1,\"id_\":\"s1\",\"pos\":[100.0,100.0],\"width\":100.0,\"height\":50.0,\"fill\":\"white\",\"stroke\":\"black\",\"text\":[{\"graph\":1,\"rId\":\"t2\",\"pos\":[100.0,100.0],\"text\":\"Graph text 2\",\"align\":\"center\",\"fill\":\"blue\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]}],\"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_\":\"p2p2\",\"points\":[[100.0,20.0],[30.0,40.0]],\"fill\":\"yellow\",\"stroke\":\"orange\",\"isRegion\":true,\"source\":\"\",\"target\":\"\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]},{\"graph\":1,\"id_\":\"p2p2\",\"points\":[[100.0,20.0],[30.0,40.0]],\"fill\":\"yellow\",\"stroke\":\"orange\",\"isRegion\":true,\"source\":\"\",\"target\":\"\",\"transform\":[1.0,0.0,0.0,1.0,0.0,0.0]}],\"width\":256,\"height\":256}" + ) + ] + +-- | Run a test case (case, input JSON payload, expected JSON payload) +runSaveGraphJSONTest :: String -> BL.ByteString -> BL.ByteString -> TestTree +runSaveGraphJSONTest label inputPayload expectedPayload = + testCase label $ do + runDb clearDatabase + let graphName = "Test Graph Name" + _ <- runServerPartWith Controllers.Graph.saveGraphJSON $ mockPutRequest "/graph-save" [("nameData", T.unpack graphName), ("jsonData", BL.unpack inputPayload)] "" + retrievedResult <- liftIO $ Models.Graph.getGraph graphName + let expectedValue = decode expectedPayload :: Maybe Value + assertEqual ("Unexpected response for " ++ label) expectedValue retrievedResult + +-- | Run all save graph test cases +runSaveGraphJSONTests :: [TestTree] +runSaveGraphJSONTests = map(\(label, inputPayload, expectedPayload) -> runSaveGraphJSONTest label inputPayload expectedPayload) saveGraphJSONTestCases + -- | Test suite for Graph Controller Module test_graphController :: TestTree -test_graphController = withDatabase "Graph Controller tests" runIndexTests +test_graphController = withDatabase "Graph Controller tests" (runIndexTests ++ runSaveGraphJSONTests)