@holoscript/framework 6.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ALL-test-results.json +1 -0
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/ROADMAP.md +175 -0
- package/dist/AgentManifest-CB4xM-Ma.d.cts +704 -0
- package/dist/AgentManifest-CB4xM-Ma.d.ts +704 -0
- package/dist/BehaviorTree-BrBFECv5.d.cts +103 -0
- package/dist/BehaviorTree-BrBFECv5.d.ts +103 -0
- package/dist/InvisibleWallet-BB6tFvRA.d.cts +1732 -0
- package/dist/InvisibleWallet-rtRrBOA8.d.ts +1732 -0
- package/dist/OrchestratorAgent-BvWgf9uw.d.cts +798 -0
- package/dist/OrchestratorAgent-Q_CbVTmO.d.ts +798 -0
- package/dist/agents/index.cjs +4790 -0
- package/dist/agents/index.d.cts +1788 -0
- package/dist/agents/index.d.ts +1788 -0
- package/dist/agents/index.js +4695 -0
- package/dist/ai/index.cjs +5347 -0
- package/dist/ai/index.d.cts +1753 -0
- package/dist/ai/index.d.ts +1753 -0
- package/dist/ai/index.js +5244 -0
- package/dist/behavior.cjs +449 -0
- package/dist/behavior.d.cts +130 -0
- package/dist/behavior.d.ts +130 -0
- package/dist/behavior.js +407 -0
- package/dist/economy/index.cjs +3659 -0
- package/dist/economy/index.d.cts +747 -0
- package/dist/economy/index.d.ts +747 -0
- package/dist/economy/index.js +3617 -0
- package/dist/implementations-D9T3un9D.d.cts +236 -0
- package/dist/implementations-D9T3un9D.d.ts +236 -0
- package/dist/index.cjs +24550 -0
- package/dist/index.d.cts +1729 -0
- package/dist/index.d.ts +1729 -0
- package/dist/index.js +24277 -0
- package/dist/learning/index.cjs +219 -0
- package/dist/learning/index.d.cts +104 -0
- package/dist/learning/index.d.ts +104 -0
- package/dist/learning/index.js +189 -0
- package/dist/negotiation/index.cjs +970 -0
- package/dist/negotiation/index.d.cts +610 -0
- package/dist/negotiation/index.d.ts +610 -0
- package/dist/negotiation/index.js +931 -0
- package/dist/skills/index.cjs +1118 -0
- package/dist/skills/index.d.cts +289 -0
- package/dist/skills/index.d.ts +289 -0
- package/dist/skills/index.js +1079 -0
- package/dist/swarm/index.cjs +5268 -0
- package/dist/swarm/index.d.cts +2433 -0
- package/dist/swarm/index.d.ts +2433 -0
- package/dist/swarm/index.js +5221 -0
- package/dist/training/index.cjs +2745 -0
- package/dist/training/index.d.cts +1734 -0
- package/dist/training/index.d.ts +1734 -0
- package/dist/training/index.js +2687 -0
- package/extract-failures.js +10 -0
- package/package.json +82 -0
- package/src/__tests__/bounty-marketplace.test.ts +374 -0
- package/src/__tests__/delegation.test.ts +144 -0
- package/src/__tests__/distributed-claimer.test.ts +147 -0
- package/src/__tests__/done-log-audit.test.ts +342 -0
- package/src/__tests__/framework.test.ts +865 -0
- package/src/__tests__/goal-synthesizer.test.ts +236 -0
- package/src/__tests__/presence.test.ts +223 -0
- package/src/__tests__/protocol-agent.test.ts +254 -0
- package/src/__tests__/revenue-splitter.test.ts +114 -0
- package/src/__tests__/scenario-driven-todo.test.ts +197 -0
- package/src/__tests__/self-improve.test.ts +349 -0
- package/src/__tests__/service-lifecycle.test.ts +237 -0
- package/src/__tests__/skill-router.test.ts +121 -0
- package/src/agents/AgentManifest.ts +493 -0
- package/src/agents/AgentRegistry.ts +475 -0
- package/src/agents/AgentTypes.ts +585 -0
- package/src/agents/AgentWalletRegistry.ts +83 -0
- package/src/agents/AuthenticatedCRDT.ts +388 -0
- package/src/agents/CapabilityMatcher.ts +453 -0
- package/src/agents/CrossRealityHandoff.ts +305 -0
- package/src/agents/CulturalMemory.ts +454 -0
- package/src/agents/FederatedRegistryAdapter.ts +429 -0
- package/src/agents/NormEngine.ts +450 -0
- package/src/agents/OrchestratorAgent.ts +414 -0
- package/src/agents/SkillWorkflowEngine.ts +472 -0
- package/src/agents/TaskDelegationService.ts +551 -0
- package/src/agents/__tests__/AgentManifest.prod.test.ts +134 -0
- package/src/agents/__tests__/AgentManifest.test.ts +182 -0
- package/src/agents/__tests__/AgentModule.test.ts +864 -0
- package/src/agents/__tests__/AgentRegistry.prod.test.ts +125 -0
- package/src/agents/__tests__/AgentRegistry.test.ts +148 -0
- package/src/agents/__tests__/AgentTypes.test.ts +534 -0
- package/src/agents/__tests__/AgentWalletRegistry.test.ts +152 -0
- package/src/agents/__tests__/AuthenticatedCRDT.test.ts +558 -0
- package/src/agents/__tests__/CapabilityMatcher.prod.test.ts +117 -0
- package/src/agents/__tests__/CapabilityMatcher.test.ts +178 -0
- package/src/agents/__tests__/CrossRealityHandoff.test.ts +402 -0
- package/src/agents/__tests__/CulturalMemory.test.ts +200 -0
- package/src/agents/__tests__/FederatedRegistryAdapter.test.ts +409 -0
- package/src/agents/__tests__/NormEngine.test.ts +276 -0
- package/src/agents/__tests__/OrchestratorAgent.test.ts +182 -0
- package/src/agents/__tests__/SkillWorkflowEngine.test.ts +357 -0
- package/src/agents/__tests__/TaskDelegationService.test.ts +446 -0
- package/src/agents/index.ts +107 -0
- package/src/agents/spatial-comms/Layer1RealTime.ts +621 -0
- package/src/agents/spatial-comms/Layer2A2A.ts +661 -0
- package/src/agents/spatial-comms/Layer3MCP.ts +651 -0
- package/src/agents/spatial-comms/ProtocolTypes.ts +543 -0
- package/src/agents/spatial-comms/SpatialCommClient.ts +483 -0
- package/src/agents/spatial-comms/__tests__/performance-benchmark.test.ts +465 -0
- package/src/agents/spatial-comms/examples/multi-agent-world-creation.ts +409 -0
- package/src/agents/spatial-comms/index.ts +66 -0
- package/src/ai/AIAdapter.ts +313 -0
- package/src/ai/AICopilot.ts +331 -0
- package/src/ai/AIOutputValidator.ts +203 -0
- package/src/ai/BTNodes.ts +239 -0
- package/src/ai/BehaviorSelector.ts +135 -0
- package/src/ai/BehaviorTree.ts +153 -0
- package/src/ai/Blackboard.ts +165 -0
- package/src/ai/GenerationAnalytics.ts +461 -0
- package/src/ai/GenerationCache.ts +265 -0
- package/src/ai/GoalPlanner.ts +165 -0
- package/src/ai/HoloScriptGenerator.ts +580 -0
- package/src/ai/InfluenceMap.ts +180 -0
- package/src/ai/NavMesh.ts +168 -0
- package/src/ai/PerceptionSystem.ts +178 -0
- package/src/ai/PromptTemplates.ts +453 -0
- package/src/ai/SemanticSearchService.ts +80 -0
- package/src/ai/StateMachine.ts +196 -0
- package/src/ai/SteeringBehavior.ts +150 -0
- package/src/ai/SteeringBehaviors.ts +244 -0
- package/src/ai/TrainingDataGenerator.ts +1082 -0
- package/src/ai/UtilityAI.ts +145 -0
- package/src/ai/__tests__/AIAdapter.prod.test.ts +259 -0
- package/src/ai/__tests__/AIAdapter.test.ts +109 -0
- package/src/ai/__tests__/AICopilot.prod.test.ts +341 -0
- package/src/ai/__tests__/AICopilot.test.ts +178 -0
- package/src/ai/__tests__/AIOutputValidator.prod.test.ts +226 -0
- package/src/ai/__tests__/AIOutputValidator.test.ts +138 -0
- package/src/ai/__tests__/BTNodes.prod.test.ts +391 -0
- package/src/ai/__tests__/BTNodes.test.ts +263 -0
- package/src/ai/__tests__/BehaviorSelector.prod.test.ts +129 -0
- package/src/ai/__tests__/BehaviorSelector.test.ts +132 -0
- package/src/ai/__tests__/BehaviorTree.prod.test.ts +266 -0
- package/src/ai/__tests__/BehaviorTree.test.ts +216 -0
- package/src/ai/__tests__/Blackboard.prod.test.ts +339 -0
- package/src/ai/__tests__/Blackboard.test.ts +183 -0
- package/src/ai/__tests__/GenerationAnalytics.prod.test.ts +141 -0
- package/src/ai/__tests__/GenerationAnalytics.test.ts +165 -0
- package/src/ai/__tests__/GenerationCache.prod.test.ts +144 -0
- package/src/ai/__tests__/GenerationCache.test.ts +171 -0
- package/src/ai/__tests__/GoalPlanner.prod.test.ts +189 -0
- package/src/ai/__tests__/GoalPlanner.test.ts +137 -0
- package/src/ai/__tests__/GoalPlannerDepth.prod.test.ts +217 -0
- package/src/ai/__tests__/HoloScriptGenerator.test.ts +125 -0
- package/src/ai/__tests__/InfluenceMap.prod.test.ts +146 -0
- package/src/ai/__tests__/InfluenceMap.test.ts +149 -0
- package/src/ai/__tests__/NavMesh.prod.test.ts +141 -0
- package/src/ai/__tests__/NavMesh.test.ts +159 -0
- package/src/ai/__tests__/PerceptionSystem.prod.test.ts +135 -0
- package/src/ai/__tests__/PerceptionSystem.test.ts +250 -0
- package/src/ai/__tests__/PromptTemplates.prod.test.ts +313 -0
- package/src/ai/__tests__/PromptTemplates.test.ts +146 -0
- package/src/ai/__tests__/SemanticSearch.test.ts +37 -0
- package/src/ai/__tests__/StateMachine.prod.test.ts +162 -0
- package/src/ai/__tests__/StateMachine.test.ts +163 -0
- package/src/ai/__tests__/SteeringBehavior.prod.test.ts +251 -0
- package/src/ai/__tests__/SteeringBehavior.test.ts +135 -0
- package/src/ai/__tests__/SteeringBehaviors.prod.test.ts +133 -0
- package/src/ai/__tests__/SteeringBehaviors.test.ts +151 -0
- package/src/ai/__tests__/TrainingDataGenerator.prod.test.ts +286 -0
- package/src/ai/__tests__/TrainingDataGenerator.test.ts +286 -0
- package/src/ai/__tests__/UtilityAI.prod.test.ts +207 -0
- package/src/ai/__tests__/UtilityAI.test.ts +155 -0
- package/src/ai/__tests__/adapters.prod.test.ts +263 -0
- package/src/ai/__tests__/adapters.test.ts +320 -0
- package/src/ai/adapters.ts +1585 -0
- package/src/ai/index.ts +130 -0
- package/src/behavior/BehaviorPresets.ts +140 -0
- package/src/behavior/BehaviorTree.ts +236 -0
- package/src/behavior/StateMachine.ts +176 -0
- package/src/behavior/StateTrait.ts +67 -0
- package/src/behavior/index.ts +8 -0
- package/src/behavior.ts +8 -0
- package/src/board/audit.ts +284 -0
- package/src/board/board-ops.ts +336 -0
- package/src/board/board-types.ts +302 -0
- package/src/board/index.ts +69 -0
- package/src/define-agent.ts +46 -0
- package/src/define-team.ts +33 -0
- package/src/delegation.ts +265 -0
- package/src/distributed-claimer.ts +228 -0
- package/src/economy/AgentBudgetEnforcer.ts +464 -0
- package/src/economy/BountyManager.ts +185 -0
- package/src/economy/CreatorRevenueAggregator.ts +460 -0
- package/src/economy/InvisibleWallet.ts +82 -0
- package/src/economy/KnowledgeMarketplace.ts +193 -0
- package/src/economy/PaymentWebhookService.ts +512 -0
- package/src/economy/RevenueSplitter.ts +156 -0
- package/src/economy/SubscriptionManager.ts +546 -0
- package/src/economy/UnifiedBudgetOptimizer.ts +635 -0
- package/src/economy/UsageMeter.ts +440 -0
- package/src/economy/_core-stubs.ts +219 -0
- package/src/economy/index.ts +100 -0
- package/src/economy/x402-facilitator.ts +1978 -0
- package/src/index.ts +348 -0
- package/src/knowledge/__tests__/knowledge-consolidator.test.ts +444 -0
- package/src/knowledge/__tests__/knowledge-store-vector.test.ts +291 -0
- package/src/knowledge/brain.ts +167 -0
- package/src/knowledge/consolidation.ts +581 -0
- package/src/knowledge/knowledge-consolidator.ts +510 -0
- package/src/knowledge/knowledge-store.ts +616 -0
- package/src/learning/MemoryConsolidator.ts +102 -0
- package/src/learning/MemoryScorer.ts +69 -0
- package/src/learning/ProceduralCompiler.ts +45 -0
- package/src/learning/SemanticClusterer.ts +66 -0
- package/src/learning/index.ts +8 -0
- package/src/llm/llm-adapter.ts +159 -0
- package/src/mesh/index.ts +309 -0
- package/src/negotiation/NegotiationProtocol.ts +694 -0
- package/src/negotiation/NegotiationTypes.ts +473 -0
- package/src/negotiation/VotingMechanisms.ts +691 -0
- package/src/negotiation/index.ts +49 -0
- package/src/protocol/goal-synthesizer.ts +317 -0
- package/src/protocol/implementations.ts +474 -0
- package/src/protocol/micro-phase-decomposer.ts +299 -0
- package/src/protocol/micro-step-decomposer.test.ts +306 -0
- package/src/protocol-agent.test.ts +353 -0
- package/src/protocol-agent.ts +670 -0
- package/src/self-improve/absorb-scanner.ts +252 -0
- package/src/self-improve/evolution-engine.ts +149 -0
- package/src/self-improve/framework-absorber.ts +214 -0
- package/src/self-improve/index.ts +50 -0
- package/src/self-improve/prompt-optimizer.ts +212 -0
- package/src/self-improve/test-generator.ts +175 -0
- package/src/skill-router.ts +186 -0
- package/src/skills/index.ts +5 -0
- package/src/skills/skill-md-bridge.ts +1699 -0
- package/src/swarm/ACOEngine.ts +261 -0
- package/src/swarm/CollectiveIntelligence.ts +383 -0
- package/src/swarm/ContributionSynthesizer.ts +481 -0
- package/src/swarm/LeaderElection.ts +393 -0
- package/src/swarm/PSOEngine.ts +206 -0
- package/src/swarm/QuorumPolicy.ts +173 -0
- package/src/swarm/SwarmCoordinator.ts +335 -0
- package/src/swarm/SwarmManager.ts +442 -0
- package/src/swarm/SwarmMembership.ts +456 -0
- package/src/swarm/VotingRound.ts +255 -0
- package/src/swarm/__tests__/ACOEngine.prod.test.ts +164 -0
- package/src/swarm/__tests__/ACOEngine.test.ts +117 -0
- package/src/swarm/__tests__/CollectiveIntelligence.prod.test.ts +296 -0
- package/src/swarm/__tests__/CollectiveIntelligence.test.ts +457 -0
- package/src/swarm/__tests__/ContributionSynthesizer.prod.test.ts +269 -0
- package/src/swarm/__tests__/ContributionSynthesizer.test.ts +254 -0
- package/src/swarm/__tests__/LeaderElection.prod.test.ts +196 -0
- package/src/swarm/__tests__/LeaderElection.test.ts +151 -0
- package/src/swarm/__tests__/PSOEngine.prod.test.ts +162 -0
- package/src/swarm/__tests__/PSOEngine.test.ts +106 -0
- package/src/swarm/__tests__/QuorumPolicy.prod.test.ts +216 -0
- package/src/swarm/__tests__/QuorumPolicy.test.ts +177 -0
- package/src/swarm/__tests__/SwarmCoordinator.prod.test.ts +186 -0
- package/src/swarm/__tests__/SwarmCoordinator.test.ts +167 -0
- package/src/swarm/__tests__/SwarmManager.prod.test.ts +308 -0
- package/src/swarm/__tests__/SwarmManager.test.ts +373 -0
- package/src/swarm/__tests__/SwarmMembership.prod.test.ts +273 -0
- package/src/swarm/__tests__/SwarmMembership.test.ts +264 -0
- package/src/swarm/__tests__/VotingRound.prod.test.ts +233 -0
- package/src/swarm/__tests__/VotingRound.test.ts +174 -0
- package/src/swarm/analytics/SwarmInspector.ts +476 -0
- package/src/swarm/analytics/SwarmMetrics.ts +449 -0
- package/src/swarm/analytics/__tests__/SwarmInspector.prod.test.ts +366 -0
- package/src/swarm/analytics/__tests__/SwarmInspector.test.ts +454 -0
- package/src/swarm/analytics/__tests__/SwarmMetrics.prod.test.ts +254 -0
- package/src/swarm/analytics/__tests__/SwarmMetrics.test.ts +370 -0
- package/src/swarm/analytics/index.ts +7 -0
- package/src/swarm/index.ts +69 -0
- package/src/swarm/messaging/BroadcastChannel.ts +509 -0
- package/src/swarm/messaging/GossipProtocol.ts +565 -0
- package/src/swarm/messaging/SwarmEventBus.ts +443 -0
- package/src/swarm/messaging/__tests__/BroadcastChannel.prod.test.ts +331 -0
- package/src/swarm/messaging/__tests__/BroadcastChannel.test.ts +333 -0
- package/src/swarm/messaging/__tests__/GossipProtocol.prod.test.ts +356 -0
- package/src/swarm/messaging/__tests__/GossipProtocol.test.ts +437 -0
- package/src/swarm/messaging/__tests__/SwarmEventBus.prod.test.ts +191 -0
- package/src/swarm/messaging/__tests__/SwarmEventBus.test.ts +247 -0
- package/src/swarm/messaging/index.ts +8 -0
- package/src/swarm/spatial/FlockingBehavior.ts +462 -0
- package/src/swarm/spatial/FormationController.ts +500 -0
- package/src/swarm/spatial/Vector3.ts +170 -0
- package/src/swarm/spatial/ZoneClaiming.ts +509 -0
- package/src/swarm/spatial/__tests__/FlockingBehavior.prod.test.ts +239 -0
- package/src/swarm/spatial/__tests__/FlockingBehavior.test.ts +298 -0
- package/src/swarm/spatial/__tests__/FormationController.prod.test.ts +240 -0
- package/src/swarm/spatial/__tests__/FormationController.test.ts +297 -0
- package/src/swarm/spatial/__tests__/Vector3.prod.test.ts +283 -0
- package/src/swarm/spatial/__tests__/Vector3.test.ts +224 -0
- package/src/swarm/spatial/__tests__/ZoneClaiming.prod.test.ts +246 -0
- package/src/swarm/spatial/__tests__/ZoneClaiming.test.ts +374 -0
- package/src/swarm/spatial/index.ts +28 -0
- package/src/team.ts +1245 -0
- package/src/training/LRScheduler.ts +377 -0
- package/src/training/QualityScoringPipeline.ts +139 -0
- package/src/training/SoftDedup.ts +461 -0
- package/src/training/SparsityMonitor.ts +685 -0
- package/src/training/SparsityMonitorTypes.ts +209 -0
- package/src/training/SpatialTrainingDataGenerator.ts +1526 -0
- package/src/training/SpatialTrainingDataTypes.ts +216 -0
- package/src/training/TrainingPipelineConfig.ts +215 -0
- package/src/training/constants.ts +94 -0
- package/src/training/index.ts +138 -0
- package/src/training/schema.ts +147 -0
- package/src/training/scripts/generate-novel-use-cases-dataset.ts +272 -0
- package/src/training/scripts/generate-spatial-dataset.ts +521 -0
- package/src/training/training/data/novel-use-cases.jsonl +153 -0
- package/src/training/training/data/spatial-reasoning-10k.jsonl +9354 -0
- package/src/training/trainingmonkey/TrainingMonkeyIntegration.ts +477 -0
- package/src/training/trainingmonkey/TrainingMonkeyTypes.ts +230 -0
- package/src/training/trainingmonkey/index.ts +26 -0
- package/src/training/trait-mappings.ts +157 -0
- package/src/types/core-stubs.d.ts +113 -0
- package/src/types.ts +304 -0
- package/test-output.txt +0 -0
- package/test-result.json +1 -0
- package/tsc-errors.txt +4 -0
- package/tsc_output.txt +0 -0
- package/tsconfig.json +14 -0
- package/tsup-learning-esm.config.ts +12 -0
- package/tsup.config.ts +21 -0
- package/typescript-errors-2.txt +0 -0
- package/typescript-errors.txt +22 -0
- package/vitest-log-utf8.txt +268 -0
- package/vitest-log.txt +0 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BTNodes.prod.test.ts — Sprint CLXX
|
|
3
|
+
*
|
|
4
|
+
* Production tests for all behavior-tree node types exported from BTNodes.ts.
|
|
5
|
+
*
|
|
6
|
+
* Node types:
|
|
7
|
+
* Composite: SequenceNode, SelectorNode, ParallelNode
|
|
8
|
+
* Decorator: InverterNode, RepeaterNode, GuardNode
|
|
9
|
+
* Leaf: ActionNode, ConditionNode, WaitNode
|
|
10
|
+
*
|
|
11
|
+
* BTStatus: 'success' | 'failure' | 'running' | 'ready'
|
|
12
|
+
* BTContext: { blackboard: { get, set }, deltaTime: number, entity: string }
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { describe, it, expect } from 'vitest';
|
|
16
|
+
import {
|
|
17
|
+
SequenceNode,
|
|
18
|
+
SelectorNode,
|
|
19
|
+
ParallelNode,
|
|
20
|
+
InverterNode,
|
|
21
|
+
RepeaterNode,
|
|
22
|
+
GuardNode,
|
|
23
|
+
ActionNode,
|
|
24
|
+
ConditionNode,
|
|
25
|
+
WaitNode,
|
|
26
|
+
} from '../BTNodes';
|
|
27
|
+
import type { BTContext, BTStatus } from '../BTNodes';
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Helpers
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
const store: Record<string, unknown> = {};
|
|
34
|
+
|
|
35
|
+
function makeCtx(deltaTime = 0.016, entity = 'agent'): BTContext {
|
|
36
|
+
const bb = new Map<string, unknown>();
|
|
37
|
+
return {
|
|
38
|
+
blackboard: {
|
|
39
|
+
get: (k: string) => bb.get(k),
|
|
40
|
+
set: (k: string, v: unknown) => {
|
|
41
|
+
bb.set(k, v);
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
deltaTime,
|
|
45
|
+
entity,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function always(status: BTStatus) {
|
|
50
|
+
return new ActionNode(`always-${status}`, () => status);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// ActionNode
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
|
|
57
|
+
describe('ActionNode', () => {
|
|
58
|
+
it('returns the status from the action callback', () => {
|
|
59
|
+
const node = always('success');
|
|
60
|
+
expect(node.tick(makeCtx())).toBe('success');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('can return failure', () => {
|
|
64
|
+
expect(always('failure').tick(makeCtx())).toBe('failure');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('can return running', () => {
|
|
68
|
+
expect(always('running').tick(makeCtx())).toBe('running');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('stores returned status in node.status', () => {
|
|
72
|
+
const n = always('success');
|
|
73
|
+
n.tick(makeCtx());
|
|
74
|
+
expect(n.status).toBe('success');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('context is passed to the action', () => {
|
|
78
|
+
let received: BTContext | null = null;
|
|
79
|
+
const n = new ActionNode('spy', (ctx) => {
|
|
80
|
+
received = ctx;
|
|
81
|
+
return 'success';
|
|
82
|
+
});
|
|
83
|
+
const ctx = makeCtx();
|
|
84
|
+
n.tick(ctx);
|
|
85
|
+
expect(received).toBe(ctx);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// ConditionNode
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
describe('ConditionNode', () => {
|
|
94
|
+
it('returns success when condition is true', () => {
|
|
95
|
+
const n = new ConditionNode('cond', () => true);
|
|
96
|
+
expect(n.tick(makeCtx())).toBe('success');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('returns failure when condition is false', () => {
|
|
100
|
+
const n = new ConditionNode('cond', () => false);
|
|
101
|
+
expect(n.tick(makeCtx())).toBe('failure');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('condition receives context', () => {
|
|
105
|
+
let got: BTContext | null = null;
|
|
106
|
+
const n = new ConditionNode('spy', (ctx) => {
|
|
107
|
+
got = ctx;
|
|
108
|
+
return true;
|
|
109
|
+
});
|
|
110
|
+
const ctx = makeCtx();
|
|
111
|
+
n.tick(ctx);
|
|
112
|
+
expect(got).toBe(ctx);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
// WaitNode
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
|
|
120
|
+
describe('WaitNode', () => {
|
|
121
|
+
it('returns running before duration elapsed', () => {
|
|
122
|
+
const n = new WaitNode('wait', 2); // 2-second wait
|
|
123
|
+
expect(n.tick(makeCtx(0.5))).toBe('running');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('returns success once duration elapsed', () => {
|
|
127
|
+
const n = new WaitNode('wait', 0.5);
|
|
128
|
+
n.tick(makeCtx(0.3));
|
|
129
|
+
expect(n.tick(makeCtx(0.3))).toBe('success');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('resets elapsed on success so next tick restarts', () => {
|
|
133
|
+
const n = new WaitNode('wait', 0.5);
|
|
134
|
+
n.tick(makeCtx(0.5)); // success, elapsed reset
|
|
135
|
+
expect(n.tick(makeCtx(0.1))).toBe('running'); // not done yet
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('reset() resets elapsed', () => {
|
|
139
|
+
const n = new WaitNode('wait', 0.5);
|
|
140
|
+
n.tick(makeCtx(0.4)); // almost done
|
|
141
|
+
n.reset();
|
|
142
|
+
expect(n.tick(makeCtx(0.2))).toBe('running'); // restarted
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
// SequenceNode
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
|
|
150
|
+
describe('SequenceNode', () => {
|
|
151
|
+
it('returns success when all children succeed', () => {
|
|
152
|
+
const n = new SequenceNode('seq', [always('success'), always('success')]);
|
|
153
|
+
expect(n.tick(makeCtx())).toBe('success');
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('returns failure on first failing child', () => {
|
|
157
|
+
const n = new SequenceNode('seq', [always('success'), always('failure'), always('success')]);
|
|
158
|
+
expect(n.tick(makeCtx())).toBe('failure');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('returns running when a child is running', () => {
|
|
162
|
+
const n = new SequenceNode('seq', [always('success'), always('running'), always('success')]);
|
|
163
|
+
expect(n.tick(makeCtx())).toBe('running');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('short-circuits after failure (later children not ticked)', () => {
|
|
167
|
+
let thirdCalled = false;
|
|
168
|
+
const n = new SequenceNode('seq', [
|
|
169
|
+
always('failure'),
|
|
170
|
+
new ActionNode('spy', () => {
|
|
171
|
+
thirdCalled = true;
|
|
172
|
+
return 'success';
|
|
173
|
+
}),
|
|
174
|
+
]);
|
|
175
|
+
n.tick(makeCtx());
|
|
176
|
+
expect(thirdCalled).toBe(false);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('reset() clears currentIndex', () => {
|
|
180
|
+
const n = new SequenceNode('seq', [always('running'), always('success')]);
|
|
181
|
+
n.tick(makeCtx()); // paused at first
|
|
182
|
+
n.reset();
|
|
183
|
+
// After reset, starts fresh — first child is 'running' again
|
|
184
|
+
expect(n.tick(makeCtx())).toBe('running');
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('empty sequence returns success', () => {
|
|
188
|
+
const n = new SequenceNode('seq', []);
|
|
189
|
+
expect(n.tick(makeCtx())).toBe('success');
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
// SelectorNode
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
|
|
197
|
+
describe('SelectorNode', () => {
|
|
198
|
+
it('returns success on first successful child', () => {
|
|
199
|
+
const n = new SelectorNode('sel', [always('failure'), always('success')]);
|
|
200
|
+
expect(n.tick(makeCtx())).toBe('success');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('returns failure when all children fail', () => {
|
|
204
|
+
const n = new SelectorNode('sel', [always('failure'), always('failure')]);
|
|
205
|
+
expect(n.tick(makeCtx())).toBe('failure');
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('returns running when a child is running', () => {
|
|
209
|
+
const n = new SelectorNode('sel', [always('failure'), always('running')]);
|
|
210
|
+
expect(n.tick(makeCtx())).toBe('running');
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('short-circuits after success (later children not ticked)', () => {
|
|
214
|
+
let laterCalled = false;
|
|
215
|
+
const n = new SelectorNode('sel', [
|
|
216
|
+
always('success'),
|
|
217
|
+
new ActionNode('spy', () => {
|
|
218
|
+
laterCalled = true;
|
|
219
|
+
return 'success';
|
|
220
|
+
}),
|
|
221
|
+
]);
|
|
222
|
+
n.tick(makeCtx());
|
|
223
|
+
expect(laterCalled).toBe(false);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('empty selector returns failure', () => {
|
|
227
|
+
const n = new SelectorNode('sel', []);
|
|
228
|
+
expect(n.tick(makeCtx())).toBe('failure');
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// ---------------------------------------------------------------------------
|
|
233
|
+
// ParallelNode
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
|
|
236
|
+
describe('ParallelNode', () => {
|
|
237
|
+
it('returns success when all children succeed (all required)', () => {
|
|
238
|
+
const n = new ParallelNode('par', [always('success'), always('success')]);
|
|
239
|
+
expect(n.tick(makeCtx())).toBe('success');
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('returns running if not enough successes yet', () => {
|
|
243
|
+
const n = new ParallelNode('par', [always('running'), always('running')]);
|
|
244
|
+
expect(n.tick(makeCtx())).toBe('running');
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('partial-success threshold: 1 of 2 sufficient', () => {
|
|
248
|
+
const n = new ParallelNode('par', [always('success'), always('failure')], 1);
|
|
249
|
+
expect(n.tick(makeCtx())).toBe('success');
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('returns failure when too many children fail', () => {
|
|
253
|
+
// requiredSuccesses = 2, but all 2 fail → failures > (2 - 2) = 0
|
|
254
|
+
const n = new ParallelNode('par', [always('failure'), always('failure')], 2);
|
|
255
|
+
expect(n.tick(makeCtx())).toBe('failure');
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('ticks ALL children regardless of results', () => {
|
|
259
|
+
let count = 0;
|
|
260
|
+
const n = new ParallelNode('par', [
|
|
261
|
+
new ActionNode('a', () => {
|
|
262
|
+
count++;
|
|
263
|
+
return 'success';
|
|
264
|
+
}),
|
|
265
|
+
new ActionNode('b', () => {
|
|
266
|
+
count++;
|
|
267
|
+
return 'failure';
|
|
268
|
+
}),
|
|
269
|
+
]);
|
|
270
|
+
n.tick(makeCtx());
|
|
271
|
+
expect(count).toBe(2);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// ---------------------------------------------------------------------------
|
|
276
|
+
// InverterNode
|
|
277
|
+
// ---------------------------------------------------------------------------
|
|
278
|
+
|
|
279
|
+
describe('InverterNode', () => {
|
|
280
|
+
it('inverts success to failure', () => {
|
|
281
|
+
const n = new InverterNode('inv', always('success'));
|
|
282
|
+
expect(n.tick(makeCtx())).toBe('failure');
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('inverts failure to success', () => {
|
|
286
|
+
const n = new InverterNode('inv', always('failure'));
|
|
287
|
+
expect(n.tick(makeCtx())).toBe('success');
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('passes through running unchanged', () => {
|
|
291
|
+
const n = new InverterNode('inv', always('running'));
|
|
292
|
+
expect(n.tick(makeCtx())).toBe('running');
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// ---------------------------------------------------------------------------
|
|
297
|
+
// RepeaterNode
|
|
298
|
+
// ---------------------------------------------------------------------------
|
|
299
|
+
|
|
300
|
+
describe('RepeaterNode', () => {
|
|
301
|
+
it('repeats the child and returns running until done', () => {
|
|
302
|
+
const n = new RepeaterNode('rep', always('success'), 3);
|
|
303
|
+
expect(n.tick(makeCtx())).toBe('running'); // 1/3
|
|
304
|
+
expect(n.tick(makeCtx())).toBe('running'); // 2/3
|
|
305
|
+
expect(n.tick(makeCtx())).toBe('success'); // 3/3 done
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it('also repeats on child failure', () => {
|
|
309
|
+
const n = new RepeaterNode('rep', always('failure'), 2);
|
|
310
|
+
n.tick(makeCtx()); // 1/2 → running
|
|
311
|
+
expect(n.tick(makeCtx())).toBe('success'); // 2/2 done
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('infinite repeat returns running indefinitely', () => {
|
|
315
|
+
const n = new RepeaterNode('rep', always('success')); // Infinity
|
|
316
|
+
for (let i = 0; i < 100; i++) {
|
|
317
|
+
expect(n.tick(makeCtx())).toBe('running');
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it('reset() allows repeating again', () => {
|
|
322
|
+
const n = new RepeaterNode('rep', always('success'), 1);
|
|
323
|
+
n.tick(makeCtx()); // done
|
|
324
|
+
n.reset();
|
|
325
|
+
expect(n.tick(makeCtx())).toBe('success'); // done again
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// ---------------------------------------------------------------------------
|
|
330
|
+
// GuardNode
|
|
331
|
+
// ---------------------------------------------------------------------------
|
|
332
|
+
|
|
333
|
+
describe('GuardNode', () => {
|
|
334
|
+
it('returns failure when condition is false (child not ticked)', () => {
|
|
335
|
+
let childCalled = false;
|
|
336
|
+
const child = new ActionNode('child', () => {
|
|
337
|
+
childCalled = true;
|
|
338
|
+
return 'success';
|
|
339
|
+
});
|
|
340
|
+
const n = new GuardNode('guard', () => false, child);
|
|
341
|
+
expect(n.tick(makeCtx())).toBe('failure');
|
|
342
|
+
expect(childCalled).toBe(false);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('delegates to child when condition is true', () => {
|
|
346
|
+
const n = new GuardNode('guard', () => true, always('success'));
|
|
347
|
+
expect(n.tick(makeCtx())).toBe('success');
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('condition receives context', () => {
|
|
351
|
+
let received: BTContext | null = null;
|
|
352
|
+
const n = new GuardNode(
|
|
353
|
+
'guard',
|
|
354
|
+
(ctx) => {
|
|
355
|
+
received = ctx;
|
|
356
|
+
return true;
|
|
357
|
+
},
|
|
358
|
+
always('success')
|
|
359
|
+
);
|
|
360
|
+
const ctx = makeCtx();
|
|
361
|
+
n.tick(ctx);
|
|
362
|
+
expect(received).toBe(ctx);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('returns child failure when condition passes but child fails', () => {
|
|
366
|
+
const n = new GuardNode('guard', () => true, always('failure'));
|
|
367
|
+
expect(n.tick(makeCtx())).toBe('failure');
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// ---------------------------------------------------------------------------
|
|
372
|
+
// reset() base behavior
|
|
373
|
+
// ---------------------------------------------------------------------------
|
|
374
|
+
|
|
375
|
+
describe('BTNode.reset()', () => {
|
|
376
|
+
it('sets status back to ready', () => {
|
|
377
|
+
const n = always('success');
|
|
378
|
+
n.tick(makeCtx());
|
|
379
|
+
expect(n.status).toBe('success');
|
|
380
|
+
n.reset();
|
|
381
|
+
expect(n.status).toBe('ready');
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('propagates reset to children recursively', () => {
|
|
385
|
+
const child = always('success');
|
|
386
|
+
const n = new SequenceNode('seq', [child]);
|
|
387
|
+
n.tick(makeCtx());
|
|
388
|
+
n.reset();
|
|
389
|
+
expect(child.status).toBe('ready');
|
|
390
|
+
});
|
|
391
|
+
});
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
BTNode,
|
|
4
|
+
BTContext,
|
|
5
|
+
SequenceNode,
|
|
6
|
+
SelectorNode,
|
|
7
|
+
ParallelNode,
|
|
8
|
+
InverterNode,
|
|
9
|
+
RepeaterNode,
|
|
10
|
+
GuardNode,
|
|
11
|
+
ActionNode,
|
|
12
|
+
ConditionNode,
|
|
13
|
+
WaitNode,
|
|
14
|
+
} from '../BTNodes';
|
|
15
|
+
|
|
16
|
+
const ctx = (dt = 0.016): BTContext => ({
|
|
17
|
+
blackboard: { get: vi.fn(), set: vi.fn() },
|
|
18
|
+
deltaTime: dt,
|
|
19
|
+
entity: 'e',
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('BTNodes', () => {
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// ActionNode
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
describe('ActionNode', () => {
|
|
28
|
+
it('returns action result as status', () => {
|
|
29
|
+
const node = new ActionNode('act', () => 'success');
|
|
30
|
+
expect(node.tick(ctx())).toBe('success');
|
|
31
|
+
expect(node.status).toBe('success');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('can return running', () => {
|
|
35
|
+
const node = new ActionNode('act', () => 'running');
|
|
36
|
+
expect(node.tick(ctx())).toBe('running');
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// ConditionNode
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
describe('ConditionNode', () => {
|
|
45
|
+
it('true → success', () => {
|
|
46
|
+
expect(new ConditionNode('c', () => true).tick(ctx())).toBe('success');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('false → failure', () => {
|
|
50
|
+
expect(new ConditionNode('c', () => false).tick(ctx())).toBe('failure');
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// WaitNode
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
describe('WaitNode', () => {
|
|
59
|
+
it('returns running until duration elapsed', () => {
|
|
60
|
+
const w = new WaitNode('wait', 1);
|
|
61
|
+
expect(w.tick(ctx(0.5))).toBe('running');
|
|
62
|
+
expect(w.tick(ctx(0.5))).toBe('success');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('reset clears elapsed time', () => {
|
|
66
|
+
const w = new WaitNode('wait', 1);
|
|
67
|
+
w.tick(ctx(0.5));
|
|
68
|
+
w.reset();
|
|
69
|
+
expect(w.tick(ctx(0.5))).toBe('running'); // started over
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// SequenceNode
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
describe('SequenceNode', () => {
|
|
78
|
+
it('runs children in order, succeeds when all succeed', () => {
|
|
79
|
+
const order: string[] = [];
|
|
80
|
+
const seq = new SequenceNode('s', [
|
|
81
|
+
new ActionNode('1', () => {
|
|
82
|
+
order.push('1');
|
|
83
|
+
return 'success';
|
|
84
|
+
}),
|
|
85
|
+
new ActionNode('2', () => {
|
|
86
|
+
order.push('2');
|
|
87
|
+
return 'success';
|
|
88
|
+
}),
|
|
89
|
+
]);
|
|
90
|
+
expect(seq.tick(ctx())).toBe('success');
|
|
91
|
+
expect(order).toEqual(['1', '2']);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('fails immediately when a child fails', () => {
|
|
95
|
+
const seq = new SequenceNode('s', [
|
|
96
|
+
new ActionNode('ok', () => 'success'),
|
|
97
|
+
new ActionNode('fail', () => 'failure'),
|
|
98
|
+
new ActionNode('skip', () => 'success'),
|
|
99
|
+
]);
|
|
100
|
+
expect(seq.tick(ctx())).toBe('failure');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('returns running and resumes on next tick', () => {
|
|
104
|
+
let runCount = 0;
|
|
105
|
+
const seq = new SequenceNode('s', [
|
|
106
|
+
new ActionNode('a', () => 'success'),
|
|
107
|
+
new ActionNode('b', () => (++runCount < 2 ? 'running' : 'success')),
|
|
108
|
+
]);
|
|
109
|
+
expect(seq.tick(ctx())).toBe('running');
|
|
110
|
+
expect(seq.tick(ctx())).toBe('success');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// SelectorNode
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
describe('SelectorNode', () => {
|
|
119
|
+
it('succeeds on first successful child', () => {
|
|
120
|
+
const sel = new SelectorNode('s', [
|
|
121
|
+
new ActionNode('f', () => 'failure'),
|
|
122
|
+
new ActionNode('ok', () => 'success'),
|
|
123
|
+
]);
|
|
124
|
+
expect(sel.tick(ctx())).toBe('success');
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('fails when all children fail', () => {
|
|
128
|
+
const sel = new SelectorNode('s', [
|
|
129
|
+
new ActionNode('f1', () => 'failure'),
|
|
130
|
+
new ActionNode('f2', () => 'failure'),
|
|
131
|
+
]);
|
|
132
|
+
expect(sel.tick(ctx())).toBe('failure');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// ParallelNode
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
describe('ParallelNode', () => {
|
|
141
|
+
it('succeeds when enough children succeed', () => {
|
|
142
|
+
const par = new ParallelNode(
|
|
143
|
+
'p',
|
|
144
|
+
[
|
|
145
|
+
new ActionNode('a', () => 'success'),
|
|
146
|
+
new ActionNode('b', () => 'success'),
|
|
147
|
+
new ActionNode('c', () => 'failure'),
|
|
148
|
+
],
|
|
149
|
+
2
|
|
150
|
+
);
|
|
151
|
+
expect(par.tick(ctx())).toBe('success');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('fails when too many children fail', () => {
|
|
155
|
+
const par = new ParallelNode(
|
|
156
|
+
'p',
|
|
157
|
+
[new ActionNode('a', () => 'failure'), new ActionNode('b', () => 'failure')],
|
|
158
|
+
2
|
|
159
|
+
);
|
|
160
|
+
expect(par.tick(ctx())).toBe('failure');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('running when not yet decided', () => {
|
|
164
|
+
const par = new ParallelNode(
|
|
165
|
+
'p',
|
|
166
|
+
[new ActionNode('a', () => 'success'), new ActionNode('b', () => 'running')],
|
|
167
|
+
2
|
|
168
|
+
);
|
|
169
|
+
expect(par.tick(ctx())).toBe('running');
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// InverterNode
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
describe('InverterNode', () => {
|
|
178
|
+
it('inverts success to failure', () => {
|
|
179
|
+
const inv = new InverterNode('i', new ActionNode('a', () => 'success'));
|
|
180
|
+
expect(inv.tick(ctx())).toBe('failure');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('inverts failure to success', () => {
|
|
184
|
+
const inv = new InverterNode('i', new ActionNode('a', () => 'failure'));
|
|
185
|
+
expect(inv.tick(ctx())).toBe('success');
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('passes running through unchanged', () => {
|
|
189
|
+
const inv = new InverterNode('i', new ActionNode('a', () => 'running'));
|
|
190
|
+
expect(inv.tick(ctx())).toBe('running');
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
// RepeaterNode
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
|
|
198
|
+
describe('RepeaterNode', () => {
|
|
199
|
+
it('repeats child N times', () => {
|
|
200
|
+
let count = 0;
|
|
201
|
+
const rep = new RepeaterNode(
|
|
202
|
+
'r',
|
|
203
|
+
new ActionNode('a', () => {
|
|
204
|
+
count++;
|
|
205
|
+
return 'success';
|
|
206
|
+
}),
|
|
207
|
+
3
|
|
208
|
+
);
|
|
209
|
+
// Each tick increments count and returns running until limit
|
|
210
|
+
rep.tick(ctx()); // count=1, returns running
|
|
211
|
+
rep.tick(ctx()); // count=2, returns running
|
|
212
|
+
const status = rep.tick(ctx()); // count=3, returns success
|
|
213
|
+
expect(count).toBe(3);
|
|
214
|
+
expect(status).toBe('success');
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('reset clears repeat count', () => {
|
|
218
|
+
let count = 0;
|
|
219
|
+
const rep = new RepeaterNode(
|
|
220
|
+
'r',
|
|
221
|
+
new ActionNode('a', () => {
|
|
222
|
+
count++;
|
|
223
|
+
return 'success';
|
|
224
|
+
}),
|
|
225
|
+
1
|
|
226
|
+
);
|
|
227
|
+
rep.tick(ctx()); // count=1
|
|
228
|
+
rep.reset();
|
|
229
|
+
expect(rep.tick(ctx())).toBe('success'); // restarted
|
|
230
|
+
expect(count).toBe(2);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// ---------------------------------------------------------------------------
|
|
235
|
+
// GuardNode
|
|
236
|
+
// ---------------------------------------------------------------------------
|
|
237
|
+
|
|
238
|
+
describe('GuardNode', () => {
|
|
239
|
+
it('executes child when condition true', () => {
|
|
240
|
+
const guard = new GuardNode('g', () => true, new ActionNode('a', () => 'success'));
|
|
241
|
+
expect(guard.tick(ctx())).toBe('success');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('returns failure when condition false', () => {
|
|
245
|
+
const guard = new GuardNode('g', () => false, new ActionNode('a', () => 'success'));
|
|
246
|
+
expect(guard.tick(ctx())).toBe('failure');
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
// ---------------------------------------------------------------------------
|
|
251
|
+
// Reset
|
|
252
|
+
// ---------------------------------------------------------------------------
|
|
253
|
+
|
|
254
|
+
it('BTNode.reset cascades to children', () => {
|
|
255
|
+
const child = new ActionNode('a', () => 'success');
|
|
256
|
+
const seq = new SequenceNode('s', [child]);
|
|
257
|
+
seq.tick(ctx());
|
|
258
|
+
expect(seq.status).toBe('success');
|
|
259
|
+
seq.reset();
|
|
260
|
+
expect(seq.status).toBe('ready');
|
|
261
|
+
expect(child.status).toBe('ready');
|
|
262
|
+
});
|
|
263
|
+
});
|