rojo/.cursor/plans/comprehensive_plugin_tests_bdd5b47c.plan.md
Joe 4a66eff938 Add comprehensive syncback roundtrip and clean mode stress tests
- Completed the implementation of roundtrip tests to validate the syncback process, ensuring that building a project, syncing back, and rebuilding produces semantically identical outputs.
- Created a new utility module for roundtrip testing, including functions for running builds and asserting directory equality.
- Developed clean mode stress tests to verify that syncing back to a dirty directory yields identical results to syncing back to a fresh directory, covering various mutation scenarios.
- Updated existing test modules to incorporate new test cases and ensure consistency in handling standalone scripts transitioning to directory format when children are added.
2026-02-01 19:01:42 -06:00

18 KiB

name overview todos isProject
Comprehensive Plugin Tests Create comprehensive Lua plugin stress tests (.spec.lua files) covering edge cases, large-scale scenarios, and abuse testing for scripts-only mode, everything mode, and concurrent multi-developer workflows.
id content status
test-utils Create test utility modules (MockApiContext, LargeTreeGenerator, PatchGenerator) pending
id content status
diff-stress Create diff.stress.spec.lua with 25+ tests for large trees, duplicates, equality edge cases pending
id content status
apply-stress Create applyPatch.stress.spec.lua with 20+ tests for partial failures, refs, className changes pending
id content status
reify-stress Create reify.stress.spec.lua with 15+ tests for deep/wide trees, property edge cases pending
id content status
property-read Create getProperty.spec.lua with 15+ tests for all error paths and property types pending
id content status
property-write Create setProperty.spec.lua with 15+ tests for error paths and type coercion pending
id content status
decode-values Create decodeValue.spec.lua with 15+ tests for refs, types, and failure scenarios pending
id content status
encode-stress Create encodeInstance.stress.spec.lua with 25+ tests for hierarchies and all instance types pending
id content status
encode-property Create encodeProperty.spec.lua with 20+ tests for all property types pending
id content status
patchset-stress Create PatchSet.stress.spec.lua with 15+ tests for merge operations and scale pending
id content status
instancemap-stress Create InstanceMap.stress.spec.lua with 10+ tests for scale and concurrency pending
id content status
integration-sync Create syncflow.spec.lua with 15+ tests for E2E sync flow and error recovery pending
id content status
integration-twoway Create twoWaySync.spec.lua with 15+ tests for bidirectional sync and conflicts pending
id content status
scripts-only-mode Create scriptsOnly.spec.lua with tests for scripts-only mode edge cases pending
id content status
everything-mode Create everythingMode.spec.lua with tests for everything mode and all instance types pending
id content status
mode-transitions Create modeTransitions.spec.lua with tests for switching between sync modes pending
id content status
multi-developer Create multiDeveloper.spec.lua simulating concurrent changes from multiple sources pending
id content status
race-conditions Create raceConditions.spec.lua detecting race conditions during sync pending
id content status
chaos-tests Create stressPatterns.spec.lua with 20+ chaos engineering tests pending
id content status
wire-tests Update runTests.lua to include all new test modules pending
false

Comprehensive Lua Plugin Stress Tests

Vision and Philosophy

Goal: Make the plugin/API interface bulletproof for complex games. The plugin must handle abuse from real-world scenarios - huge games, many developers, concurrent Rojo setups, and chaotic Studio environments.

Testing Philosophy:

  • Tests exist to EXPOSE limitations, not just pass
  • Tests that break the system are GOOD - they're the forcing function for quality
  • Do NOT back down when tests fail - fix the underlying issue
  • A failing stress test reveals a real bug that will hit users in production

Code Quality Awareness:

  • The plugin code has been iterated by AI agents and may have accumulated "fix on fix" complexity
  • If a subsystem needs repeated patches, step back and consider a ground-up redesign
  • Example: if diff equality checks keep needing fixes, redesign the comparison logic
  • Use good judgment - don't rewrite everything, but don't just pile on fixes either

Why Abuse Testing Matters:

  • The plugin/API interface is a mega weak layer
  • It constantly crashes, nukes files, syncs incorrectly, misses files, or syncs wrong files
  • Real games have thousands of instances, concurrent developers, and unpredictable Studio behavior
  • Scripts-only mode AND everything-mode must both be bulletproof

Motivation

The Rojo plugin/API interface is a critical weak layer that must handle:

  • Scripts-only mode and everything-mode
  • Large games with many developers (potentially 10+ developers with their own Rojo setups)
  • Concurrent Rojo setups on the same game
  • Edge cases causing crashes, file nuking, incorrect syncing
  • Rapid changes during active development
  • Recovery from partial failures and network issues

Known Bug Patterns to Catch

These are the types of bugs that stress tests should expose:

Duplicate/Ambiguous Path Handling:

  • Instances with duplicate-named siblings cause ambiguous paths
  • Nested duplicates at any level of the hierarchy
  • Path uniqueness checks must walk entire ancestor chain
  • Tests should verify duplicate detection at every level

Property Equality Edge Cases:

  • Floating-point comparison (epsilon = 0.0001)
  • Color3 RGB integer comparison vs float comparison
  • CFrame/Vector3/Vector2 precision
  • NaN handling (NaN != NaN)
  • EnumItem vs number comparisons

Patch Application Failures:

  • Partial application (some properties succeed, others fail)
  • Ref properties to non-existent instances
  • ClassName changes with child migration failures
  • Instance creation failures (invalid ClassName)
  • Permission errors on protected properties

Concurrency Issues:

  • Changes during confirmation dialogue
  • Rapid bidirectional changes
  • Instance destruction during patch application
  • WebSocket messages during patch processing

Memory and Scale:

  • Large instance trees (1000+ instances)
  • Deep hierarchies (50+ levels)
  • Wide hierarchies (100+ siblings)
  • Large patches (500+ changes)

Test Files to Create

1. Reconciler Stress Tests

**plugin/src/Reconciler/diff.stress.spec.lua** - Diff algorithm edge cases and stress testing

-- Key test scenarios:
-- Large tree diffing (1000+ instances)
-- Duplicate name detection at multiple levels  
-- Ambiguous path handling (nested duplicates)
-- Floating-point property precision (epsilon edge cases)
-- Color3/CFrame/Vector equality edge cases
-- ignoreUnknownInstances with mixed trees
-- Scripts-only mode deletion rules
-- Property decode failures during diff
-- Concurrent structure changes during diff
-- NaN handling in property comparisons

**plugin/src/Reconciler/applyPatch.stress.spec.lua** - Patch application edge cases

-- Key test scenarios:
-- Large batch removals (500+ instances)
-- Large batch additions (500+ instances) 
-- Mixed add/remove/update in single patch
-- Partial application failures (some succeed, some fail)
-- Complex className changes (multi-level deep)
-- Ref property chains (A->B->C->D)
-- Circular ref detection
-- Concurrent patch application
-- Race conditions with instance destruction
-- ClassName change with failing child migration

**plugin/src/Reconciler/reify.stress.spec.lua** - Instance creation stress tests

-- Key test scenarios:
-- Deep hierarchy creation (50+ levels)
-- Wide hierarchy creation (100+ siblings)
-- Mixed success/failure in sibling creation
-- Property type coercion edge cases
-- Large property sets (50+ properties per instance)
-- Nested ref chains during creation
-- Instance creation during active ChangeHistoryService
-- Memory pressure scenarios

2. Property Handling Tests

**plugin/src/Reconciler/getProperty.spec.lua** - Property reading edge cases

-- Key test scenarios:
-- All error paths (UnknownProperty, UnreadableProperty, LackingPropertyPermissions)
-- Canonical property name resolution
-- Serialization-only property aliases
-- Permission error detection for protected properties
-- Various property types (all Roblox types)
-- Computed vs stored properties
-- Properties that changed scriptability between engine versions

**plugin/src/Reconciler/setProperty.spec.lua** - Property writing edge cases

-- Key test scenarios:
-- All error paths (UnwritableProperty, LackingPropertyPermissions)
-- Type coercion scenarios (number -> int, etc.)
-- Unknown property handling (should not error)
-- Write-only vs ReadWrite properties
-- Properties with validation (e.g., Size on Terrain)
-- Rapid sequential writes to same property

**plugin/src/Reconciler/decodeValue.spec.lua** - Value decoding stress tests

-- Key test scenarios:
-- All ref scenarios (null, valid, invalid, circular)
-- All RbxDom encoded value types
-- Decode failure scenarios
-- Malformed encoded values
-- Edge case values (huge numbers, empty strings, etc.)
-- Nested encoded structures

3. Instance Encoding Stress Tests

**plugin/src/ChangeBatcher/encodeInstance.stress.spec.lua** - Encoding for syncback

-- Key test scenarios:
-- Deep hierarchies (100 levels)
-- Wide hierarchies (1000 siblings)
-- Duplicate name handling at every level
-- All supported instance types encoding
-- Complex attribute encoding (all types)
-- Tag encoding edge cases
-- Instances with 100+ properties
-- Script encoding with special characters in Source
-- Concurrent encoding of changing instances
-- Path uniqueness checking at scale
-- Memory usage with huge instances

**plugin/src/ChangeBatcher/encodeProperty.spec.lua** - Property encoding tests

-- Key test scenarios:
-- All property types (String, Bool, Int32, Int64, Float32, Float64, etc.)
-- Vector2, Vector3, Color3 encoding
-- UDim, UDim2 encoding
-- Enum encoding
-- Attributes with all value types
-- BinaryString encoding
-- CFrame encoding precision
-- Large string properties (100KB+)
-- Unicode handling

4. PatchSet and InstanceMap Stress Tests

**plugin/src/PatchSet.stress.spec.lua** - PatchSet operations at scale

-- Key test scenarios:
-- Merging large patches (1000+ changes each)
-- Conflict resolution in merge (same ID, different changes)
-- Rapid merge operations
-- Deep equality checking on complex patches
-- countChanges/countInstances at scale
-- Serialization/deserialization of large patches

**plugin/src/InstanceMap.stress.spec.lua** - InstanceMap at scale

-- Key test scenarios:
-- Large maps (10000+ instances)
-- Rapid insert/remove cycles
-- Concurrent change callbacks
-- Memory cleanup verification
-- Pause/unpause with many instances
-- ID collision handling

5. Integration and End-to-End Tests

**plugin/src/integration/syncflow.spec.lua** - Full sync flow testing

-- Key test scenarios:
-- diff -> applyPatch -> verify cycle
-- Multiple consecutive patches
-- Interleaved local and remote changes
-- Error recovery after failed patch
-- State consistency after partial failure
-- scripts-only mode full flow
-- everything mode full flow

**plugin/src/integration/twoWaySync.spec.lua** - Two-way sync scenarios

-- Key test scenarios:
-- Rapid bidirectional changes
-- Conflict resolution (both sides change same instance)
-- Pull flow for all instance types
-- Push flow for all property types
-- Mixed push/pull in single confirm
-- Concurrent Studio changes during confirm
-- ChangeBatcher pause/resume during confirm

6. Scripts-Only vs Everything Mode Tests

plugin/src/modes/scriptsOnly.spec.lua - Scripts-only mode specific tests

-- Key test scenarios:
-- Only scripts are synced (ModuleScript, Script, LocalScript)
-- Non-script instances are preserved in Studio (not deleted)
-- Script children of non-script parents sync correctly
-- RunContext property handling for Script class
-- Source property encoding/decoding
-- ignoreUnknownInstances behavior with non-scripts
-- Nested scripts under non-script containers
-- Script deletion rules (scripts can always be deleted)
-- Format transitions (standalone <-> directory) for scripts only

plugin/src/modes/everythingMode.spec.lua - Everything mode specific tests

-- Key test scenarios:
-- All instance types sync correctly
-- Folder creation and deletion
-- Part, Model, and other non-script instances
-- Instance properties beyond Source (Anchored, Size, Color, etc.)
-- Attributes on all instance types
-- Tags on all instance types
-- .model.json generation for complex instances
-- Directory structure for instances with children
-- Meta file generation (init.meta.json5)
-- Mixed hierarchies (scripts and non-scripts)

plugin/src/modes/modeTransitions.spec.lua - Switching between modes

-- Key test scenarios:
-- Switch from scripts-only to everything mode
-- Switch from everything mode to scripts-only
-- Files created in everything mode preserved when switching to scripts-only
-- Orphan non-script files in scripts-only mode
-- State consistency after mode switch

7. Multi-Developer and Concurrent Scenarios

plugin/src/concurrent/multiDeveloper.spec.lua - Simulating multiple developers

-- Key test scenarios:
-- Rapid changes from "multiple sources" (simulated)
-- Conflicting changes to same instance
-- Interleaved add/remove operations
-- Property changes while confirmation dialogue is open
-- Patches arriving during patch application
-- WebSocket message queue overflow scenarios
-- State recovery after connection drop
-- Merge conflicts in PatchSet

plugin/src/concurrent/raceConditions.spec.lua - Race condition detection

-- Key test scenarios:
-- Instance destroyed during property read
-- Instance reparented during diff
-- InstanceMap modified during iteration
-- ChangeBatcher cycle during pause/resume
-- Confirmation callback during state transition
-- Multiple simultaneous patch applications
-- ID collision during rapid add/remove

8. Edge Case and Chaos Tests

**plugin/src/chaos/stressPatterns.spec.lua** - Chaos engineering patterns

-- Key test scenarios:
-- Random operations at high frequency
-- Alternating add/remove of same instances
-- Rapid className changes
-- Property flapping (rapid value changes)
-- Instance reparenting stress
-- Deep tree mutation during reconciliation
-- Memory pressure scenarios
-- Simulated network delays (via timing)

Test Utilities to Create

**plugin/src/testUtils/MockApiContext.lua**

-- Mock API context for testing ServeSession without real server
-- Supports:
-- - Configurable responses
-- - Error injection
-- - Delay simulation
-- - Request logging

**plugin/src/testUtils/LargeTreeGenerator.lua**

-- Generates large instance hierarchies for stress testing
-- Supports:
-- - Configurable depth and width
-- - Mixed instance types
-- - Property population
-- - Duplicate name generation (for edge case testing)

**plugin/src/testUtils/PatchGenerator.lua**

-- Generates patches for stress testing
-- Supports:
-- - Random valid patches
-- - Edge case patches (all adds, all removes, etc.)
-- - Large batch patches
-- - Invalid/malformed patches for error testing

Key Files to Modify

  • [plugin/src/runTests.lua](plugin/src/runTests.lua) - Add new test modules
  • [plugin/src/Reconciler/init.spec.lua](plugin/src/init.spec.lua) - Import stress tests

Test Categories Summary

Category File Test Count (Est.) Purpose
Diff Stress diff.stress.spec.lua 25+ Large trees, duplicates, equality
Apply Stress applyPatch.stress.spec.lua 20+ Partial failures, refs, className
Reify Stress reify.stress.spec.lua 15+ Deep/wide trees, properties
Property Read getProperty.spec.lua 15+ All error paths, types
Property Write setProperty.spec.lua 15+ All error paths, coercion
Decode Values decodeValue.spec.lua 15+ Refs, types, failures
Encode Instance encodeInstance.stress.spec.lua 25+ Hierarchies, duplicates, types
Encode Property encodeProperty.spec.lua 20+ All property types
PatchSet Stress PatchSet.stress.spec.lua 15+ Merge, scale, conflict
InstanceMap Stress InstanceMap.stress.spec.lua 10+ Scale, concurrency
Sync Flow syncflow.spec.lua 15+ E2E, modes, recovery
Two-Way Sync twoWaySync.spec.lua 15+ Bidirectional, conflicts
Scripts-Only Mode scriptsOnly.spec.lua 15+ Script-specific sync, ignoreUnknown
Everything Mode everythingMode.spec.lua 20+ All instance types, properties
Mode Transitions modeTransitions.spec.lua 10+ Switching modes, orphan handling
Multi-Developer multiDeveloper.spec.lua 15+ Concurrent changes, conflicts
Race Conditions raceConditions.spec.lua 15+ Instance destruction during ops
Chaos Tests stressPatterns.spec.lua 20+ Random ops, flapping, chaos engineering

Estimated Total: 300+ new tests

Success Criteria

Primary Goal: Expose Limitations

  1. Tests should initially FAIL if the underlying code has bugs - this is expected and good
  2. Each failing test should be tracked as a bug to fix, not a test to skip
  3. Tests that pass immediately may not be stressing the system enough

Quality Gates:

  1. All tests eventually pass in CI (after bugs are fixed)
  2. Tests catch regressions in the critical paths identified
  3. Edge cases like duplicate names, ambiguous paths are thoroughly covered
  4. Both scripts-only and everything modes have dedicated test coverage
  5. Large-scale scenarios (1000+ instances) run without timeouts
  6. Memory usage is reasonable during stress tests
  7. No "fix on fix" pattern - if a test keeps failing after patches, redesign the subsystem

Bug Tracking:

When a stress test fails, document:

  • The symptom (what failed)
  • The root cause (after investigation)
  • Whether it needs a patch or a redesign
  • Link to the fix

Anti-Patterns to Avoid

  • Skipping tests because they're "too hard to fix"
  • Adding workarounds instead of fixing root causes
  • Reducing test scope to make tests pass
  • Ignoring intermittent failures (they reveal real race conditions)