Skip to content

Commit 328c829

Browse files
feat: Implement JSON output for UnitedWeStandApp
The application now outputs the final state of the society simulation in JSON format. Changes include: - Added Circe library for JSON serialization. - Created JSON encoders for Society and Participant classes. - Modified UnitedWeStandApp to convert its result to JSON and print it to standard output. - Removed console logging from the Society class to avoid interference with JSON output. - Added a unit test to verify the correctness of the JSON output.
1 parent 91a3aac commit 328c829

File tree

5 files changed

+69
-4
lines changed

5 files changed

+69
-4
lines changed

build.sbt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,9 @@ libraryDependencies += "org.clapper" %% "grizzled-slf4j"
1111
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.1.8"
1212
libraryDependencies += "ch.qos.logback" % "logback-core" % "1.1.8"
1313
libraryDependencies += "ch.qos.logback" % "logback-access" % "1.1.8"
14+
15+
libraryDependencies ++= Seq(
16+
"io.circe" %% "circe-core" % "0.14.1",
17+
"io.circe" %% "circe-generic" % "0.14.1",
18+
"io.circe" %% "circe-parser" % "0.14.1"
19+
)

src/main/scala/JsonFormats.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import io.circe.{Encoder, Json}
2+
import io.circe.generic.semiauto._
3+
import java.util.UUID
4+
5+
// Assuming Participant and Society are in the default package (as seen from ls output)
6+
7+
object JsonFormats {
8+
// Encoder for UUID
9+
implicit val uuidEncoder: Encoder[UUID] = Encoder.encodeString.contramap[UUID](_.toString)
10+
11+
// Encoder for Participant[Boolean]
12+
implicit val participantEncoder: Encoder[Participant[Boolean]] = deriveEncoder[Participant[Boolean]]
13+
14+
// Encoder for Society[Boolean]
15+
implicit val societyEncoder: Encoder[Society[Boolean]] = deriveEncoder[Society[Boolean]]
16+
}

src/main/scala/Society.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ case class Society[T](participants: List[Participant[T]]) extends Logging {
2424
.values
2525
.toList
2626

27-
info(updatedSocietyParticipants.map(_.opinion))
27+
// info(updatedSocietyParticipants.map(_.opinion))
2828
copy(participants = updatedSocietyParticipants)
2929
}
3030
}
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import scala.util.Random
2+
import JsonFormats._ // Import your encoders
3+
import io.circe.syntax._ // For .asJson
24

35
object UnitedWeStandApp extends App {
46
import Debatable._
57

68
def runCycles(cycleCount: Int = 20): Society[Boolean] = {
79
val society = Society.create(population = 7)(() => Random.nextBoolean())
8-
9-
(1 to cycleCount).foldLeft(society)((society, _) => society.update)
10+
(1 to cycleCount).foldLeft(society)((accSociety, _) => accSociety.update)
1011
}
1112

12-
runCycles()
13+
val finalSociety = runCycles()
14+
println(finalSociety.asJson.noSpaces) // Output JSON to stdout
1315
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import org.scalatest.funsuite.AnyFunSuite
2+
import io.circe.parser.parse
3+
import io.circe.syntax._ // For .asJson
4+
import io.circe.Json // For explicit Json type usage
5+
import JsonFormats._ // Your encoders
6+
7+
// Assuming UnitedWeStandApp is in the default package
8+
// Assuming Participant and Society case classes are in the default package
9+
10+
class UnitedWeStandAppTest extends AnyFunSuite {
11+
12+
test("UnitedWeStandApp.runCycles output should be valid JSON and contain non-empty participants array") {
13+
// Directly call runCycles and serialize, to avoid capturing stdout
14+
val society = UnitedWeStandApp.runCycles(cycleCount = 5) // Use a small cycle count for testing
15+
val jsonString = society.asJson.noSpaces
16+
17+
// 1. Check if the string is valid JSON
18+
val parsedJsonResult = parse(jsonString)
19+
assert(parsedJsonResult.isRight, s"Output should be valid JSON. Parsing error: ${parsedJsonResult.left.getOrElse("")}")
20+
21+
// 2. Perform checks on the parsed JSON structure
22+
val json = parsedJsonResult.getOrElse(throw new RuntimeException("JSON parsing failed in test, this should not happen due to the assert above."))
23+
24+
// Check for the presence of the 'participants' array
25+
val participantsCursor = json.hcursor.downField("participants")
26+
assert(participantsCursor.succeeded, "JSON should contain a 'participants' field.")
27+
28+
// Check if 'participants' is an array and is non-empty
29+
val participantsArray = participantsCursor.as[List[Json]]
30+
assert(participantsArray.isRight, "'participants' field should be an array.")
31+
assert(participantsArray.getOrElse(Nil).nonEmpty, "Participants array should not be empty.")
32+
33+
// 3. Optionally, check structure of a participant (if needed, more detailed)
34+
val firstParticipantOpt = participantsArray.getOrElse(Nil).headOption
35+
assert(firstParticipantOpt.isDefined, "Participant array should have at least one participant.")
36+
37+
val firstParticipantJson = firstParticipantOpt.get
38+
assert(firstParticipantJson.hcursor.downField("uuid").as[String].isRight, "Participant should have a 'uuid' string field.")
39+
assert(firstParticipantJson.hcursor.downField("opinion").as[Boolean].isRight, "Participant should have an 'opinion' boolean field.")
40+
}
41+
}

0 commit comments

Comments
 (0)