{"id":149,"date":"2010-11-22T12:37:00","date_gmt":"2010-11-22T12:37:00","guid":{"rendered":"https:\/\/pagan-gerbil.net\/?p=149"},"modified":"2010-11-22T12:37:00","modified_gmt":"2010-11-22T12:37:00","slug":"learning-unit-testing-iv-diagrams-and-mathematics","status":"publish","type":"post","link":"https:\/\/www.pagan-gerbil.net\/?p=149","title":{"rendered":"Learning Unit Testing IV &#8211; Diagrams and Mathematics"},"content":{"rendered":"<p>Programming uses maths. It\u2019s hard to ignore sometimes, especially when modelling something that is inherently graphical and position based such as a game.<\/p>\n<p>I\u2019ve decided to give the models three different position indicators \u2013 PositionX, PositionY and PositionZ. This will describe their exact position anywhere on the board \u2013 including on a ladder, a walkway or the ground. When they are given instructions to move, they should change those position variables accordingly \u2013 but not past the maximum move distance.<\/p>\n<p>My two options are to either pass in a distance and direction (both X direction and Y direction, like an aircraft would need to navigate) or a new set of position points \u2013 in essence, saying precisely \u2018go here\u2019. In the first, the new position would need to be calculated, and in the second the distance would need to be calculated. Either way, I\u2019d need to write both sets of code at some point.<\/p>\n<p>I think that it will be easier to back-track the distance and directions from a set of positions than it would be the other way around. Transforming a simple co-ordinate of (X, Y, Z) \u2013 where X is East-West, Y is North-South and Z is Up-Down \u2013 such as (1, 2, 1) to a new position of (2, 1, 2) tells me that the model has moved up a slope to the North-East for a distance of&#8230; actually, that bit gets harder. But it\u2019s at least easy to work out with trigonometry!<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/pagan-gerbil.net\/wp-content\/uploads\/2017\/01\/image.png\" width=\"164\" height=\"166\"><\/p>\n<p>On a two-dimensional plane, it is fairly simple to work out \u2013 as on the (hastily scribbled, not-to-scale) diagram to the left. This brings back distant memories of Pythagoras \u2013 a<sup>2<\/sup> + b<sup>2<\/sup> = c<sup>2<\/sup> \u2013 or, the square root of the difference in X + the difference in Y is the distance between the two points.<\/p>\n<p>On a three-dimensional world, this is a bit more complicated \u2013 it has to be reduced to two different two-dimensional triangles to calculate the distance. The difference between two points is the \u2018difference in X\u2019 value of the second triangle. Another way of putting it would be \u221a (\u221a ((X<sub>1<\/sub> \u2013 X<sub>2<\/sub>)<sup>2<\/sup> + (Y<sub>1<\/sub> \u2013 Y<sub>2<\/sub>)<sup>2<\/sup>) + (Z<sub>1<\/sub> \u2013 Z<sub>2<\/sub>)<sup>2<\/sup>). Excellent.<\/p>\n<p>In my original example then, the answer would be 1.554 arbitrary units. I could work out the directions (angles) by using more of my dusty trigonometric knowledge, but I only need to know the distance at this point. All I need to know are some test values that give a value above a model\u2019s Movement rate and some under, so I can test the outcomes of the MoveModel method. In the case of overshooting a model\u2019s permitted movement, I think I\u2019ll throw a new exception. I can see (a few pages down the line) that the model is going to need to be able to tell it\u2019s distance from other objects anyway, so this could be checked before attempting to move the model.<\/p>\n<p>Time for a test! These values will move the model just a smidgen over 4 units.<\/p>\n<pre class=\"code\">[<span style=\"color: #2b91af\">TestMethod<\/span>]\n<span style=\"color: blue\">public void <\/span>MoveModel_CannotMoveFurtherThanModelsMovement()\n{\n<span style=\"color: blue\">bool <\/span>correctExceptionThrown = <span style=\"color: blue\">false<\/span>;\n<span style=\"color: blue\">try\n<\/span>{\n<span style=\"color: #2b91af\">Mock<\/span>&lt;<span style=\"color: #2b91af\">IGameManager<\/span>&gt; mockGameManager = <span style=\"color: blue\">new <\/span><span style=\"color: #2b91af\">Mock<\/span>&lt;<span style=\"color: #2b91af\">IGameManager<\/span>&gt;();\n<span style=\"color: #2b91af\">MovementManager <\/span>movementManager = <span style=\"color: blue\">new <\/span><span style=\"color: #2b91af\">MovementManager<\/span>(mockGameManager.Object);\n<span style=\"color: #2b91af\">Model <\/span>testModel = <span style=\"color: blue\">new <\/span><span style=\"color: #2b91af\">Model<\/span>();\ntestModel.Movement = 4;\n<span style=\"color: blue\">decimal <\/span>newX = 5;\n<span style=\"color: blue\">decimal <\/span>newY = 6;\n<span style=\"color: blue\">decimal <\/span>newZ = 3;\nmovementManager.MoveModel(testModel, newX, newY, newZ);\n}\n<span style=\"color: blue\">catch <\/span>(<span style=\"color: #2b91af\">ArgumentException <\/span>ex)\n{\ncorrectExceptionThrown = <span style=\"color: blue\">true<\/span>;\n}\n<span style=\"color: #2b91af\">Assert<\/span>.IsTrue(correctExceptionThrown);\n}<\/pre>\n<p>The code to pass this in the MovementManager is:<\/p>\n<pre class=\"code\"><span style=\"color: blue\">public void <\/span>MoveModel(<span style=\"color: #2b91af\">Model <\/span>testModel, <span style=\"color: blue\">float <\/span>newX, <span style=\"color: blue\">float <\/span>newY, <span style=\"color: blue\">float <\/span>newZ)\n{\n<span style=\"color: blue\">if <\/span>(testModel.Movement &lt; testModel.GetDistanceFrom(newX, newY, newZ))\n{\n<span style=\"color: blue\">throw new <\/span><span style=\"color: #2b91af\">ArgumentException<\/span>(<span style=\"color: #a31515\">\"The model cannot move further than it's Movement rate.\"<\/span>);\n}\n}<\/pre>\n<p>And in the Model itself is:<\/p>\n<pre class=\"code\"><span style=\"color: blue\">public float <\/span>PositionX { <span style=\"color: blue\">get<\/span>; <span style=\"color: blue\">set<\/span>; }\n<span style=\"color: blue\">public float <\/span>PositionY { <span style=\"color: blue\">get<\/span>; <span style=\"color: blue\">set<\/span>; }\n<span style=\"color: blue\">public float <\/span>PositionZ { <span style=\"color: blue\">get<\/span>; <span style=\"color: blue\">set<\/span>; }\n<span style=\"color: blue\">public double <\/span>GetDistanceFrom(<span style=\"color: blue\">float <\/span>positionX, <span style=\"color: blue\">float <\/span>positionY, <span style=\"color: blue\">float <\/span>positionZ)\n{\n<span style=\"color: blue\">double <\/span>distance = 0;\n<span style=\"color: blue\">double <\/span>differenceX = <span style=\"color: blue\">this<\/span>.PositionX - positionX;\n<span style=\"color: blue\">double <\/span>differenceY = <span style=\"color: blue\">this<\/span>.PositionY - positionY;\n<span style=\"color: blue\">double <\/span>differenceZ = <span style=\"color: blue\">this<\/span>.PositionZ - positionZ;\ndistance = <span style=\"color: #2b91af\">Math<\/span>.Sqrt(<span style=\"color: #2b91af\">Math<\/span>.Sqrt((differenceX * differenceX) + (differenceY * differenceY)) + (differenceZ * differenceZ));\n<span style=\"color: blue\">return <\/span>distance;\n}<\/pre>\n<p>I can\u2019t help but think that I am still over-designing this, but it feels about right to me. Finally, the test to ensure that the model actually does move:<\/p>\n<pre class=\"code\">[<span style=\"color: #2b91af\">TestMethod<\/span>]\n<span style=\"color: blue\">public void <\/span>MoveModel_PositionHasChanged()\n{\n<span style=\"color: #2b91af\">Mock<\/span>&lt;<span style=\"color: #2b91af\">IGameManager<\/span>&gt; mockGameManager = <span style=\"color: blue\">new <\/span><span style=\"color: #2b91af\">Mock<\/span>&lt;<span style=\"color: #2b91af\">IGameManager<\/span>&gt;();\n<span style=\"color: #2b91af\">MovementManager <\/span>movementManager = <span style=\"color: blue\">new <\/span><span style=\"color: #2b91af\">MovementManager<\/span>(mockGameManager.Object);\n<span style=\"color: #2b91af\">Model <\/span>testModel = <span style=\"color: blue\">new <\/span><span style=\"color: #2b91af\">Model<\/span>();\ntestModel.Movement = 4;\ntestModel.PositionX = 0;\ntestModel.PositionY = 0;\ntestModel.PositionZ = 0;\n<span style=\"color: blue\">float <\/span>newX = 5;\n<span style=\"color: blue\">float <\/span>newY = 4;\n<span style=\"color: blue\">float <\/span>newZ = 3;\nmovementManager.MoveModel(testModel, newX, newY, newZ);\n<span style=\"color: #2b91af\">Assert<\/span>.AreEqual(5, testModel.PositionX);\n<span style=\"color: #2b91af\">Assert<\/span>.AreEqual(4, testModel.PositionY);\n<span style=\"color: #2b91af\">Assert<\/span>.AreEqual(3, testModel.PositionZ);\n}<\/pre>\n<p>Which requires a change to MoveModel &#8211;<\/p>\n<pre class=\"code\"><span style=\"color: blue\">public void <\/span>MoveModel(<span style=\"color: #2b91af\">Model <\/span>testModel, <span style=\"color: blue\">float <\/span>newX, <span style=\"color: blue\">float <\/span>newY, <span style=\"color: blue\">float <\/span>newZ)\n{\n<span style=\"color: blue\">if <\/span>(testModel.Movement &lt; testModel.GetDistanceFrom(newX, newY, newZ))\n{\n<span style=\"color: blue\">throw new <\/span><span style=\"color: #2b91af\">ArgumentException<\/span>(<span style=\"color: #a31515\">\"The model cannot move further than it's Movement rate.\"<\/span>);\n}\n<span style=\"color: blue\">else\n<\/span>{\ntestModel.PositionX = newX;\ntestModel.PositionY = newY;\ntestModel.PositionZ = newZ;\n}\n}<\/pre>\n<p>Next, the situation that a model may move in two goes \u2013 and cannot go above it\u2019s movement rate in both chunks. For this, we\u2019ll have to record the distance moved.<\/p>\n<pre class=\"code\">[<span style=\"color: #2b91af\">TestMethod<\/span>]\n<span style=\"color: blue\">public void <\/span>MoveModel_TotalMovementInASingleTurnCannotExceedModelMovement_ThrowsArgumentException()\n{\n<span style=\"color: blue\">bool <\/span>correctExceptionThrown = <span style=\"color: blue\">false<\/span>;\n<span style=\"color: blue\">try\n<\/span>{\n<span style=\"color: #2b91af\">Mock<\/span>&lt;<span style=\"color: #2b91af\">IGameManager<\/span>&gt; mockGameManager = <span style=\"color: blue\">new <\/span><span style=\"color: #2b91af\">Mock<\/span>&lt;<span style=\"color: #2b91af\">IGameManager<\/span>&gt;();\n<span style=\"color: #2b91af\">MovementManager <\/span>movementManager = <span style=\"color: blue\">new <\/span><span style=\"color: #2b91af\">MovementManager<\/span>(mockGameManager.Object);\n<span style=\"color: #2b91af\">Model <\/span>testModel = <span style=\"color: blue\">new <\/span><span style=\"color: #2b91af\">Model<\/span>();\ntestModel.Movement = 4;\ntestModel.PositionX = 0;\ntestModel.PositionY = 0;\ntestModel.PositionZ = 0;\n<span style=\"color: blue\">float <\/span>newX = 5;\n<span style=\"color: blue\">float <\/span>newY = 4;\n<span style=\"color: blue\">float <\/span>newZ = 3;\nmovementManager.MoveModel(testModel, newX, newY, newZ);\nmovementManager.MoveModel(testModel, 0, 0, 0);\n}\n<span style=\"color: blue\">catch <\/span>(<span style=\"color: #2b91af\">ArgumentException <\/span>ex)\n{\ncorrectExceptionThrown = <span style=\"color: blue\">true<\/span>;\n}\n<span style=\"color: #2b91af\">Assert<\/span>.IsTrue(correctExceptionThrown);\n}<\/pre>\n<p>Movement of a model is starting to take on several different, inter-related actions so I\u2019m going to move that stuff into the Model class itself. MoveModel in the MovementManager becomes simpler:<\/p>\n<pre class=\"code\"><span style=\"color: blue\">public void <\/span>MoveModel(<span style=\"color: #2b91af\">Model <\/span>testModel, <span style=\"color: blue\">float <\/span>newX, <span style=\"color: blue\">float <\/span>newY, <span style=\"color: blue\">float <\/span>newZ)\n{\n<span style=\"color: blue\">if <\/span>(testModel.Movement &lt; testModel.GetDistanceFrom(newX, newY, newZ) + testModel.TotalDistanceMoved)\n{\n<span style=\"color: blue\">throw new <\/span><span style=\"color: #2b91af\">ArgumentException<\/span>(<span style=\"color: #a31515\">\"The model cannot move further than it's Movement rate.\"<\/span>);\n}\n<span style=\"color: blue\">else\n<\/span>{\ntestModel.MoveModel(newX, newY, newZ);\n}\n}<\/pre>\n<p>And the addition to the Model class is:<\/p>\n<pre class=\"code\"><span style=\"color: blue\">public double <\/span>TotalDistanceMoved { <span style=\"color: blue\">get<\/span>; <span style=\"color: blue\">private set<\/span>; }\n<span style=\"color: blue\">public void <\/span>MoveModel(<span style=\"color: blue\">float <\/span>positionX, <span style=\"color: blue\">float <\/span>positionY, <span style=\"color: blue\">float <\/span>positionZ)\n{\n<span style=\"color: blue\">this<\/span>.TotalDistanceMoved += GetDistanceFrom(positionX, positionY, positionZ);\n<span style=\"color: blue\">this<\/span>.PositionX = positionX;\n<span style=\"color: blue\">this<\/span>.PositionY = positionY;\n<span style=\"color: blue\">this<\/span>.PositionZ = positionZ;\n}<\/pre>\n<p>Next is to make sure we can make multiple moves without going over the limit and throwing an exception:<\/p>\n<pre class=\"code\">[<span style=\"color: #2b91af\">TestMethod<\/span>]\n<span style=\"color: blue\">public void <\/span>MoveModel_MakeTwoSmallMovementsWithoutGoingOverMovementRate()\n{\n<span style=\"color: blue\">bool <\/span>correctExceptionThrown = <span style=\"color: blue\">false<\/span>;\n<span style=\"color: blue\">try\n<\/span>{\n<span style=\"color: #2b91af\">Mock<\/span>&lt;<span style=\"color: #2b91af\">IGameManager<\/span>&gt; mockGameManager = <span style=\"color: blue\">new <\/span><span style=\"color: #2b91af\">Mock<\/span>&lt;<span style=\"color: #2b91af\">IGameManager<\/span>&gt;();\n<span style=\"color: #2b91af\">MovementManager <\/span>movementManager = <span style=\"color: blue\">new <\/span><span style=\"color: #2b91af\">MovementManager<\/span>(mockGameManager.Object);\n<span style=\"color: #2b91af\">Model <\/span>testModel = <span style=\"color: blue\">new <\/span><span style=\"color: #2b91af\">Model<\/span>();\ntestModel.Movement = 4;\ntestModel.PositionX = 0;\ntestModel.PositionY = 0;\ntestModel.PositionZ = 0;\n<span style=\"color: blue\">float <\/span>newX = 1;\n<span style=\"color: blue\">float <\/span>newY = 1;\n<span style=\"color: blue\">float <\/span>newZ = 0;\nmovementManager.MoveModel(testModel, newX, newY, newZ);\nmovementManager.MoveModel(testModel, 0, 0, 0);\n}\n<span style=\"color: blue\">catch <\/span>(<span style=\"color: #2b91af\">ArgumentException <\/span>ex)\n{\ncorrectExceptionThrown = <span style=\"color: blue\">true<\/span>;\n}\n<span style=\"color: #2b91af\">Assert<\/span>.IsFalse(correctExceptionThrown);\n}<\/pre>\n<p>This doesn\u2019t need any new code, it just proves that the existing code still works. Finally, there needs to be a check that the TotalDistanceMoved will reset to 0 at the beginning of each turn. What is going to be the trigger for that? Currently, the only place that a new turn is known about is the GameManager, and the IncrementPhase method. The question is, therefore, how should the model be informed of this change? Should the GameManager contain a collection of all the models currently in the game, so the IncrementPhase method can just iterate that collection? Or should models be held elsewhere, and subscribe to a \u2018new turn\u2019 event that is triggered? I think it makes most sense for the models to be stored in the GameManager \u2013 at least at this time \u2013 and for them to expose a \u2018new turn\u2019 method.<\/p>\n<p>I have become aware as I am refactoring this code around that I\u2019m starting to break the isolation of the unit tests \u2013 some tests are calling methods in classes that are not the class under test. This will obviously need to be refactored, but I\u2019d like to finish the current train of thought first. To do that properly though, I need to add a new test class \u2013 ModelTests \u2013 and test the NewTurn method on that as well as making sure the GameManager calls NewTurn on each of it\u2019s (mocked) Models. Testing these two things becomes an integration test, and cannot guarantee which class\/method is actually responsible for failing a test.<\/p>\n<pre class=\"code\">[<span style=\"color: #2b91af\">TestMethod<\/span>]\n<span style=\"color: blue\">public void <\/span>IncrementTurnPhase_CallsNewTurnOnModels()\n{\n<span style=\"color: #2b91af\">GameManager <\/span>manager = <span style=\"color: blue\">new <\/span><span style=\"color: #2b91af\">GameManager<\/span>(2);\n<span style=\"color: #2b91af\">Mock<\/span>&lt;<span style=\"color: #2b91af\">IModel<\/span>&gt; mockModel = <span style=\"color: blue\">new <\/span><span style=\"color: #2b91af\">Mock<\/span>&lt;<span style=\"color: #2b91af\">IModel<\/span>&gt;();\nmanager.Models.Add(mockModel.Object);\nmanager.IncrementPhase(); <span style=\"color: green\">\/\/ Go to Shooting\n<\/span>manager.IncrementPhase(); <span style=\"color: green\">\/\/ Go to Close Combat\n<\/span>manager.IncrementPhase(); <span style=\"color: green\">\/\/ Go to Recovery\n<\/span>manager.IncrementPhase(); <span style=\"color: green\">\/\/ Go back to Movement\n<\/span>mockModel.Verify(item =&gt; item.NewTurn(), <span style=\"color: #2b91af\">Times<\/span>.Once());\n}<\/pre>\n<p>In the above test, I am adding a mock model into the GameManager, then verifying that the NewTurn method is called once. I add the Models property, create an IModel interface, change the IncrementPhase code to<\/p>\n<pre class=\"code\"><span style=\"color: blue\">public void <\/span>IncrementPhase()\n{\n<span style=\"color: blue\">if <\/span>(CurrentPhase == <span style=\"color: #2b91af\">TurnPhase<\/span>.Recovery)\n{\nCurrentPhase = <span style=\"color: #2b91af\">TurnPhase<\/span>.Movement;\n<span style=\"color: blue\">if <\/span>(CurrentPlayersTurn == NumberOfPlayers)\n{\nCurrentPlayersTurn = 1;\n}\n<span style=\"color: blue\">else\n<\/span>{\nCurrentPlayersTurn++;\n}\n<span style=\"color: blue\">foreach <\/span>(<span style=\"color: #2b91af\">IModel <\/span>model <span style=\"color: blue\">in this<\/span>.Models)\n{\nmodel.NewTurn();\n}\n}\n<span style=\"color: blue\">else\n<\/span>{\nCurrentPhase++;\n}\n}<\/pre>\n<p>And&#8230; four tests fail on me!<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" title=\"image_3\" style=\"border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px\" border=\"0\" alt=\"image_3\" src=\"https:\/\/pagan-gerbil.net\/wp-content\/uploads\/2017\/01\/image_3.png\" width=\"512\" height=\"231\"><\/p>\n<p>This is exactly the purpose of unit testing. In this case, I realised that the Models collection (List&lt;IModel&gt;) hadn\u2019t been initialised \u2013 these tests suddenly failing are a result of the turn ending and attempting to iterate through a collection that hasn\u2019t been set up yet \u2013 a NullReferenceException. One quick tweak to the constructor, and they\u2019re all passing again.<\/p>\n<p>Finally, the test on the Model side to make sure that it sets TotalDistanceMoved back to zero.<\/p>\n<pre class=\"code\">[<span style=\"color: #2b91af\">TestClass<\/span>]\n<span style=\"color: blue\">public class <\/span><span style=\"color: #2b91af\">ModelTests\n<\/span>{\n[<span style=\"color: #2b91af\">TestMethod<\/span>]\n<span style=\"color: blue\">public void <\/span>NewTurn_SetsDistanceMovedToZero()\n{\n<span style=\"color: #2b91af\">Model <\/span>testModel = <span style=\"color: blue\">new <\/span><span style=\"color: #2b91af\">Model<\/span>();\ntestModel.TotalDistanceMoved = 4;\ntestModel.NewTurn();\n<span style=\"color: #2b91af\">Assert<\/span>.AreEqual(0, testModel.TotalDistanceMoved);\n}\n}<\/pre>\n<p>And the code in Model being insultingly simple:<\/p>\n<pre class=\"code\"><span style=\"color: blue\">public void <\/span>NewTurn()\n{\n<span style=\"color: blue\">this<\/span>.TotalDistanceMoved = 0;\n}<\/pre>\n<p>Thirteen tests so far, and all passing. A quick lesson in how unit tests can quickly point to something going awry \u2013 though I am sure more important lessons will come along as I move through the project \u2013 and we\u2019re almost to the end of page 10 in the rulebook. This has been a long section, and it\u2019s a good point to leave it at.<\/p>\n<p><em>This is a learning project I am documenting in order to teach myself TDD and unit testing \u2013 as I publish this, I have already written many parts in advance but I want to know what I\u2019m doing wrong! If you see improvements or corrections, either to the code or the process, please leave a comment and let me know! Introduction to this project can be found <a href=\"https:\/\/pagan-gerbil.net\/?p=106\">here<\/a>.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Programming uses maths. It\u2019s hard to ignore sometimes, especially when modelling something that is inherently graphical and position based such as a game. I\u2019ve decided to give the models three different position indicators \u2013 PositionX, PositionY and PositionZ. This will describe their exact position anywhere on the board \u2013 including on a ladder, a walkway &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/www.pagan-gerbil.net\/?p=149\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Learning Unit Testing IV &#8211; Diagrams and Mathematics&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[14],"tags":[43,62,63,64],"series":[73],"_links":{"self":[{"href":"https:\/\/www.pagan-gerbil.net\/index.php?rest_route=\/wp\/v2\/posts\/149"}],"collection":[{"href":"https:\/\/www.pagan-gerbil.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.pagan-gerbil.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.pagan-gerbil.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.pagan-gerbil.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=149"}],"version-history":[{"count":0,"href":"https:\/\/www.pagan-gerbil.net\/index.php?rest_route=\/wp\/v2\/posts\/149\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.pagan-gerbil.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=149"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.pagan-gerbil.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=149"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.pagan-gerbil.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=149"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/www.pagan-gerbil.net\/index.php?rest_route=%2Fwp%2Fv2%2Fseries&post=149"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}