Arcology Engine

Shared Test Fixtures

Contents

The three files in this module provide reusable test data shared across parser, indexer, editor, and domain test suites. They eliminate duplication and ensure consistent fixture data wherever org-mode structures are tested.

SampleOrgFiles

SampleOrgFiles provides static org-mode document strings for parser and indexer tests. It covers the full spectrum of real-world org content: simple headings, complex nested TODOs, unicode, tables, property-drawer-heavy nodes, malformed properties, planning timestamps, and stress-test generators.

Consumed by: parser tests, indexer tests.

kotlin#+name: fixtures-sample-org-files:tangle ../src/commonTest/kotlin/computer/whatthefuck/arcology/fixtures/SampleOrgFiles.kt
package computer.whatthefuck.arcology.fixtures

object SampleOrgFiles {

    val SIMPLE_ORG_CONTENT = """
,* Simple Heading
:PROPERTIES:
:ID: simple-heading-id
:END:

This is a simple paragraph under the heading.

,** Sub Heading

Another paragraph with some text.
""".trimIndent()

    val COMPLEX_ORG_CONTENT = """
#+TITLE: Complex Org File
#+AUTHOR: Test Author
#+DATE: 2022-01-01

,* TODO Project Planning
:PROPERTIES:
:ID: project-planning-id
:CATEGORY: project
:EFFORT: 8h
:CUSTOM_ID: planning
:ROAM_ALIASES: main-project core-project
:END:

This is a project planning heading with various properties.

,** DONE Research Phase :research:planning:
:PROPERTIES:
:ID: research-phase-id
:EFFORT: 4h
:SCHEDULED: <2022-01-01 Sat>
:DEADLINE: <2022-01-15 Sat>
:END:

Research has been completed.

,*** TODO Literature Review
:PROPERTIES:
:ID: literature-review-id
:END:

Need to review the literature.

,** TODO Implementation Phase :development:
:PROPERTIES:
:ID: implementation-id
:EFFORT: 16h
:END:

Start the implementation work.

,* Notes
:PROPERTIES:
:ID: notes-section-id
:END:

Some additional notes and [[id:project-planning-id][references]] to other sections.

External link: [[https://example.com][Example Website]]
""".trimIndent()

    val MINIMAL_ORG_CONTENT = """
,* Minimal
:PROPERTIES:
:ID: minimal-id
:END:
""".trimIndent()

    val EMPTY_ORG_CONTENT = ""

    val MALFORMED_ORG_CONTENT = """
,* Heading with broken properties
:PROPERTIES:
:ID: broken-id
:MISSING_END_TAG: value

,* Another heading

This content has malformed properties section.
""".trimIndent()

    val UNICODE_ORG_CONTENT = """
,* 中文标题 with émojis 🎉
:PROPERTIES:
:ID: unicode-heading-id
:UNICODE_PROP: Value with émojis 🚀 and ünicode
:SYMBOLS: !@#$%^&*()[]{}|;':\",./<>?
:END:

Content with unicode characters: éñüç 中文 العربية

,** Подзаголовок
:PROPERTIES:
:ID: russian-subtitle-id
:END:

Russian subtitle content.
""".trimIndent()

    val TAGS_AND_LINKS_ORG_CONTENT = """
,* Main Topic :tag1:tag2:important:
:PROPERTIES:
:ID: main-topic-id
:ROAM_TAGS: additional custom tags
:END:

This heading has multiple tags and links.

Link to [[id:other-topic-id][Other Topic]].
External: [[https://example.com][Website]]
File link: [[file:other.org][Other File]]

,** Sub Topic :tag3:
:PROPERTIES:
:ID: sub-topic-id
:END:

Another topic with [[id:main-topic-id][back reference]].
""".trimIndent()

    val PLANNING_ORG_CONTENT = """
,* TODO Task with Planning Info
:PROPERTIES:
:ID: task-with-planning-id
:END:
SCHEDULED: <2022-01-01 Sat 09:00>
DEADLINE: <2022-01-15 Sat>
CLOSED: [2022-01-10 Mon 15:30]

Task with scheduling information.

,* DONE Completed Task
:PROPERTIES:
:ID: completed-task-id
:END:
CLOSED: [2022-01-05 Wed 10:00]

This task is completed.

,* CANCELLED Cancelled Task
:PROPERTIES:
:ID: cancelled-task-id
:END:
CLOSED: [2022-01-03 Mon 14:00]

This task was cancelled.
""".trimIndent()

    val PROPERTIES_HEAVY_ORG_CONTENT = """
,* Node with Many Properties
:PROPERTIES:
:ID: property-heavy-id
:CUSTOM_ID: heavy
:CATEGORY: test
:EFFORT: 2:30
:CREATED: [2022-01-01 Sat 10:00]
:LAST_MODIFIED: [2022-01-02 Sun 15:30]
:ROAM_ALIASES: alias1 alias2 alias3
:ROAM_TAGS: tag1 tag2 tag3
:AUTHOR: Test Author
:EMAIL: test@example.com
:URL: https://example.com
:LOCATION: Test Location
:CONTEXT: work
:PROJECT: test-project
:PRIORITY: high
:STATUS: active
:END:

A node with extensive properties for testing edge cases.
""".trimIndent()

    // Utility function to create test content with dynamic IDs
    fun createTestContent(nodeId: String, title: String, level: Int = 1): String {
        val stars = "*".repeat(level)
        return """
$stars $title
:PROPERTIES:
:ID: $nodeId
:END:

Content for $title.
""".trimIndent()
    }

    val REAL_WORLD_COMPLEXITY_ORG_CONTENT = """
:PROPERTIES:
:ID: file-level-id-12345
:END:
#+TITLE: Complex Project Documentation
#+FILETAGS: :project:documentation:

This is file-level content before any headings with a link to
[[id:section-overview-id][Section Overview]] and [[https://example.com][Example Site]].

,* Overview                                                    :overview:main:
:PROPERTIES:
:ID: section-overview-id
:CATEGORY: planning
:ROAM_ALIASES: project-overview
:END:

Project overview content with various elements.

,** Goals and Objectives
:PROPERTIES:
:ID: goals-id
:EFFORT: 4:00
:END:
DEADLINE: <2024-03-01 Fri>

1. First goal
2. Second goal with [[id:task-1-id][link to task]]
3. Third goal

,** Status Table
:PROPERTIES:
:ID: status-table-id
:END:

| Phase      | Status  | Owner     |
|------------+---------+-----------|
| Planning   | Done    | Alice     |
| Design     | Active  | Bob       |

,* TODO Task Section                                          :tasks:urgent:
:PROPERTIES:
:ID: task-section-id
:END:

,** TODO [#A] High Priority Task
:PROPERTIES:
:ID: task-1-id
:EFFORT: 2:00
:GEO_COORDS: geo:37.7749,-122.4194
:END:
SCHEDULED: <2024-01-20 Sat 09:00>
DEADLINE: <2024-01-25 Thu>

Task description with:
- [ ] Checkbox item 1
- [X] Completed checkbox

,#+BEGIN_SRC kotlin
fun example() {
    println("Code block in task")
}
,#+END_SRC

,** DONE [#B] Completed Task
:PROPERTIES:
:ID: task-2-id
:END:
CLOSED: [2024-01-10 Wed 15:30]

This task is done.

,** WAITING Blocked Task                                      :blocked:
:PROPERTIES:
:ID: task-3-id
:ROAM_REFS: "https://example.com/ticket/123" "https://jira.example.com/ABC-456"
:END:

Waiting on external dependency.

,* Notes and References                                        :notes:
:PROPERTIES:
:ID: notes-section-id
:END:

,** Meeting Notes
:PROPERTIES:
:ID: meeting-notes-id
:END:

#+BEGIN_QUOTE
Important quote from meeting.
-- Speaker Name
#+END_QUOTE

,*** 2024-01-15 Standup
:PROPERTIES:
:ID: standup-id
:END:

- Discussed [[id:task-1-id][high priority task]]
- Need to coordinate with [[id:goals-id][project goals]]
""".trimIndent()

    val TABLE_HEAVY_ORG_CONTENT = """
,* Tables Test
:PROPERTIES:
:ID: tables-test-id
:END:

,** Simple Table
:PROPERTIES:
:ID: simple-table-id
:END:

| Col1 | Col2 | Col3 |
|------+------+------|
| A    | B    | C    |
| 1    | 2    | 3    |

,** Unicode Table
:PROPERTIES:
:ID: unicode-table-id
:END:

| Name   | Symbol |
|--------+--------|
| Omega  | Ω      |
| Alpha  | α      |
| Check  | ✓      |

,** Table with Formula
:PROPERTIES:
:ID: formula-table-id
:END:

| Item  | Price |
|-------+-------|
| Apple | 1.00  |
| Total | 1.00  |
#+TBLFM: @3${'$'}2=vsum(@2${'$'}2..@2${'$'}2)
""".trimIndent()

    /**
     * Creates a large, complex org file for stress testing.
     */
    fun createStressTestOrgContent(
        nodeCount: Int = 500,
        linksPerNode: Int = 3,
        nestingDepth: Int = 5,
        includeSourceBlocks: Boolean = true
    ): String {
        val builder = StringBuilder()
        builder.appendLine(":PROPERTIES:")
        builder.appendLine(":ID: stress-test-file-id")
        builder.appendLine(":END:")
        builder.appendLine("#+TITLE: Stress Test File with $nodeCount nodes")
        builder.appendLine()

        val allNodeIds = (1..nodeCount).map { "stress-node-$it-id" }
        val todos = listOf(null, "TODO", "DONE", "WAITING")

        for (i in 1..nodeCount) {
            val level = ((i - 1) % nestingDepth) + 1
            val stars = "*".repeat(level)
            val tags = (1..2).map { "tag${(i * it) % 20}" }
            val tagStr = " :${tags.joinToString(":")}:"
            val todo = todos[i % todos.size]?.let { "$it " } ?: ""

            builder.appendLine("$stars ${todo}Heading $i$tagStr")
            builder.appendLine(":PROPERTIES:")
            builder.appendLine(":ID: ${allNodeIds[i - 1]}")
            builder.appendLine(":END:")

            if (todo == "TODO ") {
                builder.appendLine("SCHEDULED: <2024-02-${(i % 28) + 1} Mon>")
            }

            builder.appendLine()
            builder.appendLine("Content for heading $i.")

            // Links to other nodes
            val linkTargets = (1..linksPerNode).map {
                allNodeIds[(i + it * 7) % nodeCount]
            }.distinct()
            linkTargets.forEach { targetId ->
                builder.appendLine("Reference: [[id:$targetId][Link to $targetId]]")
            }

            if (includeSourceBlocks && i % 10 == 0) {
                builder.appendLine()
                builder.appendLine("#+BEGIN_SRC kotlin")
                builder.appendLine("fun function$i() = println(\"$i\")")
                builder.appendLine("#+END_SRC")
            }

            builder.appendLine()
        }

        return builder.toString()
    }

    // Create a large org file for performance testing
    fun createLargeOrgContent(nodeCount: Int = 1000): String {
        val builder = StringBuilder()
        builder.appendLine("#+TITLE: Large Test File")
        builder.appendLine()

        for (i in 1..nodeCount) {
            val level = (i % 3) + 1
            val stars = "*".repeat(level)
            builder.appendLine("$stars Heading $i")
            builder.appendLine(":PROPERTIES:")
            builder.appendLine(":ID: heading-$i-id")
            if (i % 5 == 0) {
                builder.appendLine(":CATEGORY: category-${i / 5}")
            }
            if (i % 10 == 0) {
                builder.appendLine(":ROAM_TAGS: tag${i / 10}")
            }
            builder.appendLine(":END:")
            builder.appendLine()
            builder.appendLine("Content for heading $i with some text to make it realistic.")
            if (i % 20 == 0) {
                builder.appendLine("This heading has a link to [[id:heading-${i-10}-id][Previous Heading]].")
            }
            builder.appendLine()
        }

        return builder.toString()
    }
}

TestData

TestData provides typed domain model instances (OrgFile, OrgNode, OrgLink, OrgTag, OrgAlias, NodeProperty, FileProperty) for domain and database tests. EdgeCaseTestData supplies boundary-value fixtures: deeply nested nodes, empty values, unicode identifiers, and very long file paths.

Consumed by: domain tests, database tests.

kotlin#+name: fixtures-test-data:tangle ../src/commonTest/kotlin/computer/whatthefuck/arcology/fixtures/TestData.kt
package computer.whatthefuck.arcology.fixtures

import computer.whatthefuck.arcology.domain.*
import kotlinx.datetime.Instant

object TestData {

    // Sample timestamps
    val sampleTimestamp = Instant.fromEpochSeconds(1640995200) // 2022-01-01T00:00:00Z
    val laterTimestamp = Instant.fromEpochSeconds(1641081600) // 2022-01-02T00:00:00Z

    // Sample OrgFile instances
    val sampleOrgFile = OrgFile(
        path = "/test/sample.org",
        title = "Sample Org File",
        hash = "abc123",
        accessTime = sampleTimestamp,
        modificationTime = sampleTimestamp
    )

    val emptyOrgFile = OrgFile(
        path = "/test/empty.org",
        title = null,
        hash = "def456",
        accessTime = sampleTimestamp,
        modificationTime = sampleTimestamp
    )

    // Sample OrgNode instances
    val rootNode = OrgNode(
        id = "root-node-id",
        file = "/test/sample.org",
        level = 1,
        position = 0,
        title = "Root Heading",
        properties = mapOf("CUSTOM_ID" to "root", "CATEGORY" to "project"),
        outlinePath = listOf("Root Heading")
    )

    val childNode = OrgNode(
        id = "child-node-id",
        file = "/test/sample.org",
        level = 2,
        position = 1,
        todo = "TODO",
        priority = "A",
        scheduled = "2022-01-01",
        deadline = "2022-01-15",
        title = "Child Task",
        properties = mapOf("EFFORT" to "2h"),
        outlinePath = listOf("Root Heading", "Child Task")
    )

    val nodeWithComplexProperties = OrgNode(
        id = "complex-node-id",
        file = "/test/complex.org",
        level = 3,
        position = 2,
        title = "Complex Node",
        properties = mapOf(
            "ID" to "complex-node-id",
            "ROAM_ALIASES" to "alias1 alias2",
            "ROAM_TAGS" to "tag1 tag2 tag3",
            "CREATED" to "2022-01-01",
            "LAST_MODIFIED" to "2022-01-02"
        ),
        outlinePath = listOf("Root", "Section", "Complex Node")
    )

    // Sample Links
    val sampleLink = OrgLink(
        position = 42,
        fromNode = "root-node-id",
        toNode = "child-node-id",
        type = "internal",
        properties = mapOf("description" to "Link to child")
    )

    val externalLink = OrgLink(
        position = 84,
        fromNode = "child-node-id",
        toNode = null,
        type = "external",
        properties = mapOf("url" to "https://example.com", "description" to "Example link")
    )

    // Sample Tags
    val projectTag = OrgTag(nodeId = "root-node-id", tag = "project")
    val urgentTag = OrgTag(nodeId = "child-node-id", tag = "urgent")
    val workTag = OrgTag(nodeId = "child-node-id", tag = "work")

    // Sample Aliases
    val rootAlias = OrgAlias(nodeId = "root-node-id", alias = "main-project")
    val childAlias = OrgAlias(nodeId = "child-node-id", alias = "important-task")

    // Sample Properties
    val customIdProperty = NodeProperty(nodeId = "root-node-id", key = "CUSTOM_ID", value = "root")
    val effortProperty = NodeProperty(nodeId = "child-node-id", key = "EFFORT", value = "2h")
    val categoryProperty = NodeProperty(nodeId = "root-node-id", key = "CATEGORY", value = "project")

    val titleFileProperty = FileProperty(file = "/test/sample.org", key = "TITLE", value = "Sample File")
    val authorFileProperty = FileProperty(file = "/test/sample.org", key = "AUTHOR", value = "Test Author")

    // Collections for batch operations
    val allNodes = listOf(rootNode, childNode, nodeWithComplexProperties)
    val allLinks = listOf(sampleLink, externalLink)
    val allTags = listOf(projectTag, urgentTag, workTag)
    val allAliases = listOf(rootAlias, childAlias)
    val allNodeProperties = listOf(customIdProperty, effortProperty, categoryProperty)
    val allFileProperties = listOf(titleFileProperty, authorFileProperty)
}

object EdgeCaseTestData {

    // Edge case data for testing boundaries
    val fileWithLongPath = OrgFile(
        path = "/very/long/path/that/might/cause/issues/in/some/systems/with/limited/path/length/support.org",
        title = "File with very long path",
        hash = "long-hash-value-123456789",
        accessTime = TestData.sampleTimestamp,
        modificationTime = TestData.sampleTimestamp
    )

    val nodeWithMaxLevel = OrgNode(
        id = "deep-node-id",
        file = "/test/deep.org",
        level = 20, // Very deep nesting
        position = 999,
        title = "Very Deep Node",
        outlinePath = (1..20).map { "Level $it" }
    )

    val nodeWithEmptyValues = OrgNode(
        id = "empty-node-id",
        file = "/test/empty.org",
        level = 1,
        position = 0,
        todo = "",
        priority = "",
        scheduled = "",
        deadline = "",
        title = "",
        properties = emptyMap(),
        outlinePath = emptyList()
    )

    val nodeWithSpecialCharacters = OrgNode(
        id = "special-chars-éñü-节点",
        file = "/test/unicode.org",
        level = 1,
        position = 0,
        title = "Special Characters: éñüç 中文 العربية 🎉",
        properties = mapOf(
            "UNICODE_PROP" to "Value with émojis 🚀 and ünicode",
            "SYMBOLS" to "!@#$%^&*()[]{}|;':\",./<>?"
        ),
        outlinePath = listOf("Root", "Special Characters: éñüç 中文 العربية 🎉")
    )
}

OrgContentGenerators

OrgContentGenerators provides Kotest Arb generators for property-based testing of the parser. It generates random org headings, links, timestamps, body paragraphs, and full documents to ensure the parser handles arbitrary input without crashing or producing incorrect structure.

Consumed by: parser property tests, editor property tests.

kotlin#+name: fixtures-org-content-generators:tangle ../src/commonTest/kotlin/computer/whatthefuck/arcology/generators/OrgContentGenerators.kt
package computer.whatthefuck.arcology.generators

import io.kotest.property.Arb
import io.kotest.property.arbitrary.*

/**
 * Kotest Arb generators for org-mode content structures.
 * Used in property-based tests to fuzz the parser.
 */
object OrgContentGenerators {

    /** Heading levels 1-10 (stars) */
    val headingLevel: Arb<Int> = Arb.int(1..10)

    /** Standard TODO states plus null for no state */
    val todoState: Arb<String?> = Arb.element(
        null, "TODO", "DONE", "WAITING", "CANCELLED", "NEXT", "SOMEDAY"
    )

    /** Priority markers */
    val priority: Arb<String?> = Arb.element(null, "[#A]", "[#B]", "[#C]")

    /** Valid UUID-style IDs */
    val validId: Arb<String> = Arb.uuid().map { it.toString() }

    /** Simple alphanumeric tag */
    val tag: Arb<String> = Arb.string(2..15, Codepoint.alphanumeric())

    /** Tag list for inline heading tags */
    val tagList: Arb<List<String>> = Arb.list(tag, 0..5)

    /** Safe heading title text (no newlines, no leading stars) */
    val headingTitle: Arb<String> = Arb.string(3..80, Codepoint.printableAscii())
        .filter { !it.contains("\n") && !it.startsWith("*") && !it.endsWith(":") }

    /** Property key (uppercase alphanumeric with underscores) */
    val propertyKey: Arb<String> = Arb.string(2..20, Codepoint.alphanumeric())
        .map { it.uppercase() }

    /** Property value (single line, no colons at start) */
    val propertyValue: Arb<String> = Arb.string(1..50, Codepoint.printableAscii())
        .filter { !it.contains("\n") }

    /** Complete org heading with optional properties drawer */
    val orgHeading: Arb<OrgHeadingGen> = Arb.bind(
        headingLevel,
        todoState,
        priority,
        headingTitle,
        tagList,
        validId.orNull(0.3)
    ) { level, todo, pri, title, tags, id ->
        OrgHeadingGen(level, todo, pri, title, tags, id)
    }

    /** ID link: [[id:uuid][description]] */
    val idLink: Arb<String> = Arb.bind(validId, headingTitle) { id, desc ->
        "[[id:$id][${desc.take(30)}]]"
    }

    /** File link: [[file:path.org][description]] */
    val fileLink: Arb<String> = Arb.string(3..20, Codepoint.alphanumeric())
        .map { "[[file:$it.org][$it]]" }

    /** HTTPS link */
    val httpsLink: Arb<String> = Arb.string(3..20, Codepoint.alphanumeric())
        .map { "[[https://example.com/$it][Example $it]]" }

    /** Any link type */
    val anyLink: Arb<String> = Arb.choice(idLink, fileLink, httpsLink)

    /** Org date timestamp: <YYYY-MM-DD> */
    val orgDate: Arb<String> = Arb.bind(
        Arb.int(2020..2030),
        Arb.int(1..12),
        Arb.int(1..28)
    ) { y, m, d ->
        "<$y-${m.toString().padStart(2, '0')}-${d.toString().padStart(2, '0')}>"
    }

    /** Org date with time: <YYYY-MM-DD HH:MM> */
    val orgDateWithTime: Arb<String> = Arb.bind(
        Arb.int(2020..2030),
        Arb.int(1..12),
        Arb.int(1..28),
        Arb.int(0..23),
        Arb.int(0..59)
    ) { y, mo, d, h, mi ->
        "<$y-${mo.toString().padStart(2, '0')}-${d.toString().padStart(2, '0')} " +
            "${h.toString().padStart(2, '0')}:${mi.toString().padStart(2, '0')}>"
    }

    /** Paragraph of body text with optional links */
    val bodyParagraph: Arb<String> = Arb.bind(
        Arb.string(20..200, Codepoint.printableAscii()).filter { !it.contains("\n") },
        anyLink.orNull(0.5)
    ) { text, link ->
        if (link != null) "$text $link" else text
    }

    /**
     * Generate a complete small org document with N headings.
     */
    fun orgDocument(headingCount: Int = 5): Arb<String> = Arb.bind(
        Arb.list(orgHeading, headingCount..headingCount),
        Arb.list(bodyParagraph, headingCount..headingCount)
    ) { headings, bodies ->
        val builder = StringBuilder()
        builder.appendLine("#+TITLE: Generated Test Document")
        builder.appendLine()
        headings.zip(bodies).forEach { (heading, body) ->
            builder.appendLine(heading.render())
            builder.appendLine(body)
            builder.appendLine()
        }
        builder.toString()
    }

    /**
     * Generate a level-0 (file-level) org document with properties.
     */
    fun level0Document(): Arb<String> = Arb.bind(
        Arb.list(OrgContentGenerators.propertyValue, 1..5),
        Arb.string(3..50)
    ) { propValues, title ->
        val props = propValues.joinToString("\n") { ":VALUE: $it" }
        """:PROPERTIES:
$props
:END:
#+title: $title

Body content.

,* First Sub-heading
Content under first sub.

,* Second Sub-heading
Content under second sub.
"""
    }

    /**
     * Generate a level-0 org document with deeply nested sub-headings.
     */
    fun level0WithNestedChildren(maxDepth: Int = 5): Arb<String> = Arb.bind(
        Arb.int(1..maxDepth),
        Arb.string(3..50)
    ) { depth, title ->
        val content = """:PROPERTIES:
:ID: test-id
:END:
#+title: $title

Body.

,* Level 1
""".trimIndent()

        val nested = (2..depth).joinToString("\n") { level ->
            val stars = "*".repeat(level)
            "$stars Level $level\nContent at level $level.\n"
        }
        content + nested
    }
}

/**
 * Generated org heading for property tests.
 */
data class OrgHeadingGen(
    val level: Int,
    val todo: String?,
    val priority: String?,
    val title: String,
    val tags: List<String>,
    val id: String?
) {
    fun render(): String {
        val stars = "*".repeat(level)
        val todoStr = todo?.let { "$it " } ?: ""
        val priorityStr = priority?.let { "$it " } ?: ""
        val tagsStr = if (tags.isNotEmpty()) " :${tags.joinToString(":")}:" else ""
        val titleStr = title
        val propsBlock = if (id != null) {
            "\n:PROPERTIES:\n:ID: $id\n:END:"
        } else ""

        return "$stars $todoStr$priorityStr$titleStr$tagsStr$propsBlock"
    }
}