ace-support-test-helpers 0.13.0
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +110 -0
- data/LICENSE +21 -0
- data/README.md +29 -0
- data/Rakefile +14 -0
- data/lib/ace/test_support/base_test_case.rb +37 -0
- data/lib/ace/test_support/cli_helpers.rb +103 -0
- data/lib/ace/test_support/config_helpers.rb +235 -0
- data/lib/ace/test_support/coverage.rb +156 -0
- data/lib/ace/test_support/fixtures/bundle_mocks.rb +243 -0
- data/lib/ace/test_support/fixtures/git_mocks.rb +237 -0
- data/lib/ace/test_support/fixtures/http_mocks.rb +202 -0
- data/lib/ace/test_support/fixtures/prompt_helpers.rb +59 -0
- data/lib/ace/test_support/fixtures/test_runner_mocks.rb +86 -0
- data/lib/ace/test_support/performance_helpers.rb +178 -0
- data/lib/ace/test_support/subprocess_runner.rb +65 -0
- data/lib/ace/test_support/test_environment.rb +152 -0
- data/lib/ace/test_support/test_helper.rb +105 -0
- data/lib/ace/test_support/version.rb +7 -0
- data/lib/ace/test_support.rb +31 -0
- metadata +94 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f6dc0530e9109d683e0b5e41530b9ff50c45aa646ecf9b289f45ad95dbe8b908
|
|
4
|
+
data.tar.gz: dea7a118cb5b6b8f8c06792a54c6400dd362836222dcccf00ea29ca537d5489c
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 896cecde3a9fa17f925742f5005009004e374597ea1fc4e0d26889627420987e4a98612e9095456c5c9bcee6c439eabc2726b2d01aec3ab078ff02c360fda6ec
|
|
7
|
+
data.tar.gz: 81227f0ce16964e340c9fb8fa07d0dc7e7294dd5da226b598d2f84fa0632aebca6b88d21ce042bd4ddc6e393953bcd3739f122539ca2e4f7e8aab6d389a612d8
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to ace-test-support will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.13.0] - 2026-03-23
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Corrected `source_code_uri` gemspec metadata to point to package-specific path instead of repository root.
|
|
14
|
+
|
|
15
|
+
### Technical
|
|
16
|
+
- Removed phantom `handbook/**/*` glob from gemspec (no handbook directory exists).
|
|
17
|
+
|
|
18
|
+
## [0.12.6] - 2026-03-22
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
- Refreshed README structure for support-library consistency while preserving existing usage and utility documentation.
|
|
22
|
+
- Added the canonical `Part of ACE` footer and explicit `License` section to the package README.
|
|
23
|
+
|
|
24
|
+
## [0.12.5] - 2026-03-18
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- Migrated CLI namespace from `Ace::Core::CLI::*` to `Ace::Support::Cli::*` (ace-support-cli is now the canonical home for CLI infrastructure).
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## [0.12.4] - 2026-03-09
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
- `with_temp_dir` now resets cached `Ace::Bundle` configuration alongside project-root cache state so temp-dir tests remain order-independent under full non-E2E suite execution.
|
|
34
|
+
|
|
35
|
+
## [0.12.3] - 2026-03-04
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
- `with_cascade_configs` now isolates HOME into a temporary directory when creating home-level config fixtures, preventing permission errors in sandboxed environments.
|
|
39
|
+
|
|
40
|
+
## [0.12.2] - 2026-01-31
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
- Add `respond_to?(:get)` check to `test_stub_ace_core_config_integration` skip condition
|
|
44
|
+
|
|
45
|
+
## [0.12.1] - 2026-01-31
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
- Improve `stub_ace_core_config` isolation with `respond_to?` guard and `define_singleton_method`
|
|
49
|
+
|
|
50
|
+
## [0.11.1] - 2026-01-15
|
|
51
|
+
|
|
52
|
+
### Changed
|
|
53
|
+
- **Context Mocks Migration**: Updated ContextMocks to use Ace::Bundle
|
|
54
|
+
- Renamed all `Ace::Context` references to `Ace::Bundle`
|
|
55
|
+
- Updated mock methods: `stub_load_file`, `stub_load_auto`, `restore_load_file`, `restore_load_auto`
|
|
56
|
+
- Comments now reference ace-bundle instead of ace-context
|
|
57
|
+
- Updated TestRunnerMocks default package from ace-context to ace-bundle
|
|
58
|
+
|
|
59
|
+
### Technical
|
|
60
|
+
- Updated contract tests to check for Ace::Bundle availability
|
|
61
|
+
|
|
62
|
+
## [0.11.0] - 2026-01-07
|
|
63
|
+
|
|
64
|
+
### Added
|
|
65
|
+
- **CLI test helpers** for dry-cli based CLIs (task 179)
|
|
66
|
+
- `CliHelpers` module with reusable patterns for testing CLI commands
|
|
67
|
+
- `invoke_cli` - Invoke CLI and capture stdout/stderr/result
|
|
68
|
+
- `invoke_cli_stdout` - Convenience method for stdout only
|
|
69
|
+
- `assert_cli_success` - Assert CLI returns exit code 0
|
|
70
|
+
- `assert_cli_output_matches` - Assert CLI output matches pattern
|
|
71
|
+
|
|
72
|
+
## [0.10.0] - 2026-01-03
|
|
73
|
+
|
|
74
|
+
### Changed
|
|
75
|
+
- **BREAKING**: Minimum Ruby version raised to 3.3.0 (was 3.2.0)
|
|
76
|
+
- Standardized gemspec file patterns with deterministic Dir.glob
|
|
77
|
+
- Added MIT LICENSE file
|
|
78
|
+
|
|
79
|
+
## [0.9.3] - 2025-12-27
|
|
80
|
+
|
|
81
|
+
### Changed
|
|
82
|
+
|
|
83
|
+
- **Contract Test Enhancement**: Added guarded require for ace-git in git contract tests
|
|
84
|
+
- Enables integration test to exercise CommandExecutor stub with ace-git when available
|
|
85
|
+
- Part of ace-git-diff to ace-git migration
|
|
86
|
+
|
|
87
|
+
## [0.9.2] - 2025-10-08
|
|
88
|
+
|
|
89
|
+
### Changed
|
|
90
|
+
|
|
91
|
+
- **Test Structure Migration**: Migrated to flat ATOM structure
|
|
92
|
+
- From: `test/unit/atoms/` and `test/unit/molecules/`
|
|
93
|
+
- To: `test/atoms/` and `test/molecules/`
|
|
94
|
+
- Aligns with standardized test organization across all ACE packages
|
|
95
|
+
- Simplifies test discovery and maintenance
|
|
96
|
+
|
|
97
|
+
## [0.9.1] - 2025-10-08
|
|
98
|
+
|
|
99
|
+
### Changed
|
|
100
|
+
- **Test directory structure**: Reorganized tests to follow ATOM architecture
|
|
101
|
+
- Moved tests from flat `test/*.rb` structure to `test/unit/atoms/` and `test/unit/molecules/`
|
|
102
|
+
- Makes tests discoverable by ace-test-runner
|
|
103
|
+
- Aligns with project-wide ATOM architecture pattern (ADR-011)
|
|
104
|
+
- Files organized as:
|
|
105
|
+
- `test/unit/atoms/`: base_test_case_test.rb, test_helper_test.rb
|
|
106
|
+
- `test/unit/molecules/`: config_helpers_test.rb, test_environment_test.rb
|
|
107
|
+
|
|
108
|
+
## [0.9.0] - 2025-10-05
|
|
109
|
+
|
|
110
|
+
Initial release with shared test utilities for ACE ecosystem.
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Michal Czyz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1> ACE - Support Test Helpers </h1>
|
|
3
|
+
|
|
4
|
+
Shared test harness and environment helpers for ACE packages.
|
|
5
|
+
|
|
6
|
+
<img src="https://raw.githubusercontent.com/cs3b/ace/main/docs/brand/AgenticCodingEnvironment.Logo.XS.jpg" alt="ACE Logo" width="480">
|
|
7
|
+
<br><br>
|
|
8
|
+
|
|
9
|
+
<a href="https://rubygems.org/gems/ace-support-test-helpers"><img alt="Gem Version" src="https://img.shields.io/gem/v/ace-support-test-helpers.svg" /></a>
|
|
10
|
+
<a href="https://www.ruby-lang.org"><img alt="Ruby" src="https://img.shields.io/badge/Ruby-3.2+-CC342D?logo=ruby" /></a>
|
|
11
|
+
<a href="https://opensource.org/licenses/MIT"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg" /></a>
|
|
12
|
+
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
> Works with: Claude Code, Codex CLI, OpenCode, Gemini CLI, pi-agent, and more.
|
|
16
|
+
|
|
17
|
+
`ace-support-test-helpers` provides reusable helpers for temporary environment setup, assertions, and integration-friendly test scaffolding so that ACE packages share consistent test patterns without duplicating boilerplate.
|
|
18
|
+
|
|
19
|
+
## Use Cases
|
|
20
|
+
|
|
21
|
+
**Write consistent package tests** - share base test case abstractions, setup patterns, and assertions across `ace-support-*` and other ACE packages via common helper modules.
|
|
22
|
+
|
|
23
|
+
**Build integration test flows** - isolate environment variables, filesystem state, and config fixtures with reliable temporary-directory and override helpers used alongside [ace-test-runner](../ace-test-runner).
|
|
24
|
+
|
|
25
|
+
**Improve CI stability** - reduce flaky test behavior with shared deterministic helpers for directory management, configuration, and environment cleanup.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
Part of [ACE](https://github.com/cs3b/ace)
|
data/Rakefile
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "minitest/test_task"
|
|
5
|
+
|
|
6
|
+
desc "Run tests using ace-test"
|
|
7
|
+
task :test do
|
|
8
|
+
sh "ace-test"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
desc "Run tests directly (CI mode)"
|
|
12
|
+
Minitest::TestTask.create(:ci)
|
|
13
|
+
|
|
14
|
+
task default: :test
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "minitest/test"
|
|
4
|
+
|
|
5
|
+
module Ace
|
|
6
|
+
module TestSupport
|
|
7
|
+
# Base test case class with common utilities for all ace-* gems
|
|
8
|
+
class BaseTestCase < Minitest::Test
|
|
9
|
+
include TestHelper
|
|
10
|
+
include SubprocessRunner
|
|
11
|
+
|
|
12
|
+
def fixture_path(path)
|
|
13
|
+
File.expand_path("fixtures/#{path}", File.dirname(caller_locations(1, 1)[0].path))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def setup
|
|
17
|
+
@original_pwd = Dir.pwd
|
|
18
|
+
super
|
|
19
|
+
# Enable test mode by default - tests needing real config use with_real_config
|
|
20
|
+
if defined?(Ace::Support::Config) && Ace::Support::Config.respond_to?(:test_mode=)
|
|
21
|
+
Ace::Support::Config.test_mode = true
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def teardown
|
|
26
|
+
Dir.chdir(@original_pwd) if @original_pwd
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Alias for backward compatibility
|
|
32
|
+
AceTestCase = BaseTestCase
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Make AceTestCase available at top level for convenience
|
|
37
|
+
AceTestCase = Ace::TestSupport::AceTestCase
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ace
|
|
4
|
+
module TestSupport
|
|
5
|
+
# CLI test helpers for ace-support-cli based CLIs
|
|
6
|
+
#
|
|
7
|
+
# This module provides reusable patterns for testing CLI commands
|
|
8
|
+
# during the Thor to ace-support-cli migration (Task 179).
|
|
9
|
+
#
|
|
10
|
+
# @example Usage in test file
|
|
11
|
+
# require 'ace/test_support'
|
|
12
|
+
#
|
|
13
|
+
# class CliRoutingTest < Minitest::Test
|
|
14
|
+
# include Ace::TestSupport::CliHelpers
|
|
15
|
+
#
|
|
16
|
+
# def test_routes_to_version
|
|
17
|
+
# result = invoke_cli(MyGem::CLI, ["--version"])
|
|
18
|
+
# assert_match(/\d+\.\d+\.\d+/, result[:stdout])
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
module CliHelpers
|
|
22
|
+
# Invoke a ace-support-cli CLI and capture output
|
|
23
|
+
#
|
|
24
|
+
# This helper provides a consistent pattern for testing CLI routing
|
|
25
|
+
# and command execution. It wraps CLI.start with capture_io to
|
|
26
|
+
# capture stdout/stderr.
|
|
27
|
+
#
|
|
28
|
+
# @param cli_class [Class] The CLI module/class with a .start method
|
|
29
|
+
# @param args [Array<String>] Command line arguments
|
|
30
|
+
# @return [Hash] Result hash with :stdout, :stderr, :result keys
|
|
31
|
+
#
|
|
32
|
+
# @example Basic usage
|
|
33
|
+
# result = invoke_cli(Ace::Search::CLI, ["version"])
|
|
34
|
+
# assert_match(/\d+\.\d+/, result[:stdout])
|
|
35
|
+
#
|
|
36
|
+
# @example Testing with options
|
|
37
|
+
# result = invoke_cli(Ace::Search::CLI, ["search", "TODO", "--max-results", "10"])
|
|
38
|
+
# assert_equal 0, result[:result]
|
|
39
|
+
#
|
|
40
|
+
# @note ace-support-cli calls exit(0) for --help, so we catch SystemExit.
|
|
41
|
+
# Commands raise Ace::Support::Cli::Error for controlled failures
|
|
42
|
+
# (exception-based exit code pattern per ADR-023).
|
|
43
|
+
def invoke_cli(cli_class, args)
|
|
44
|
+
stdout, stderr = capture_io do
|
|
45
|
+
@_cli_result = cli_class.start(args)
|
|
46
|
+
rescue SystemExit => e
|
|
47
|
+
@_cli_result = e.status
|
|
48
|
+
rescue Ace::Support::Cli::Error => e
|
|
49
|
+
warn e.message
|
|
50
|
+
@_cli_result = e.exit_code
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
{
|
|
54
|
+
stdout: stdout,
|
|
55
|
+
stderr: stderr,
|
|
56
|
+
result: @_cli_result
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Invoke CLI and return only stdout (convenience method)
|
|
61
|
+
#
|
|
62
|
+
# @param cli_class [Class] The CLI module/class with a .start method
|
|
63
|
+
# @param args [Array<String>] Command line arguments
|
|
64
|
+
# @return [String] Standard output
|
|
65
|
+
#
|
|
66
|
+
# @example
|
|
67
|
+
# output = invoke_cli_stdout(Ace::Search::CLI, ["version"])
|
|
68
|
+
# assert_match(/\d+\.\d+/, output)
|
|
69
|
+
def invoke_cli_stdout(cli_class, args)
|
|
70
|
+
invoke_cli(cli_class, args)[:stdout]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Assert CLI returns success (exit code 0)
|
|
74
|
+
#
|
|
75
|
+
# @param cli_class [Class] The CLI module/class with a .start method
|
|
76
|
+
# @param args [Array<String>] Command line arguments
|
|
77
|
+
# @param message [String, nil] Optional assertion message
|
|
78
|
+
#
|
|
79
|
+
# @example
|
|
80
|
+
# assert_cli_success(Ace::Search::CLI, ["version"])
|
|
81
|
+
def assert_cli_success(cli_class, args, message = nil)
|
|
82
|
+
result = invoke_cli(cli_class, args)
|
|
83
|
+
assert_equal 0, result[:result],
|
|
84
|
+
message || "Expected CLI to return 0, got #{result[:result]}. stderr: #{result[:stderr]}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Assert CLI output matches pattern
|
|
88
|
+
#
|
|
89
|
+
# @param cli_class [Class] The CLI module/class with a .start method
|
|
90
|
+
# @param args [Array<String>] Command line arguments
|
|
91
|
+
# @param pattern [Regexp, String] Pattern to match against stdout
|
|
92
|
+
# @param message [String, nil] Optional assertion message
|
|
93
|
+
#
|
|
94
|
+
# @example
|
|
95
|
+
# assert_cli_output_matches(Ace::Search::CLI, ["version"], /\d+\.\d+/)
|
|
96
|
+
def assert_cli_output_matches(cli_class, args, pattern, message = nil)
|
|
97
|
+
result = invoke_cli(cli_class, args)
|
|
98
|
+
assert_match pattern, result[:stdout],
|
|
99
|
+
message || "Expected stdout to match #{pattern.inspect}"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "tmpdir"
|
|
6
|
+
|
|
7
|
+
module Ace
|
|
8
|
+
module TestSupport
|
|
9
|
+
# Helpers for config testing across all ace-* gems
|
|
10
|
+
module ConfigHelpers
|
|
11
|
+
# Execute block with test mode enabled
|
|
12
|
+
#
|
|
13
|
+
# This helper enables Ace::Support::Config test mode for the duration of the block,
|
|
14
|
+
# skipping filesystem searches and returning mock config instead.
|
|
15
|
+
#
|
|
16
|
+
# @param mock_config [Hash] Mock configuration data to return (default: {})
|
|
17
|
+
# @yield Block to execute with test mode enabled
|
|
18
|
+
# @return [Object] Result of the block
|
|
19
|
+
#
|
|
20
|
+
# @example Skip config filesystem access
|
|
21
|
+
# with_test_config do
|
|
22
|
+
# config = Ace::Support::Config.create.resolve
|
|
23
|
+
# assert_equal({}, config.data)
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# @example Provide mock config
|
|
27
|
+
# with_test_config({ "key" => "value" }) do
|
|
28
|
+
# config = Ace::Support::Config.create.resolve
|
|
29
|
+
# assert_equal "value", config.get("key")
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
def with_test_config(mock_config = {})
|
|
33
|
+
require "ace/support/config"
|
|
34
|
+
|
|
35
|
+
original_test_mode = Ace::Support::Config.test_mode
|
|
36
|
+
original_mock = Ace::Support::Config.default_mock
|
|
37
|
+
|
|
38
|
+
Ace::Support::Config.test_mode = true
|
|
39
|
+
Ace::Support::Config.default_mock = mock_config
|
|
40
|
+
|
|
41
|
+
yield
|
|
42
|
+
ensure
|
|
43
|
+
Ace::Support::Config.test_mode = original_test_mode
|
|
44
|
+
Ace::Support::Config.default_mock = original_mock
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Execute block with real config (test mode disabled)
|
|
48
|
+
#
|
|
49
|
+
# This helper temporarily disables test mode for integration tests
|
|
50
|
+
# that need to test actual filesystem-based config loading.
|
|
51
|
+
#
|
|
52
|
+
# @yield Block to execute with real config
|
|
53
|
+
# @return [Object] Result of the block
|
|
54
|
+
#
|
|
55
|
+
# @example Run integration test with real config
|
|
56
|
+
# with_real_config do
|
|
57
|
+
# with_temp_config(".git" => "", ".ace" => { "config.yml" => "key: value" }) do
|
|
58
|
+
# config = Ace::Support::Config.create.resolve
|
|
59
|
+
# assert_equal "value", config.get("key")
|
|
60
|
+
# end
|
|
61
|
+
# end
|
|
62
|
+
#
|
|
63
|
+
def with_real_config
|
|
64
|
+
require "ace/support/config"
|
|
65
|
+
|
|
66
|
+
original_test_mode = Ace::Support::Config.test_mode
|
|
67
|
+
|
|
68
|
+
Ace::Support::Config.test_mode = false
|
|
69
|
+
|
|
70
|
+
yield
|
|
71
|
+
ensure
|
|
72
|
+
Ace::Support::Config.test_mode = original_test_mode
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Execute block with temporary config file
|
|
76
|
+
def with_config(path, content)
|
|
77
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
78
|
+
|
|
79
|
+
# Handle both Hash and String content
|
|
80
|
+
file_content = case content
|
|
81
|
+
when Hash
|
|
82
|
+
content.to_yaml
|
|
83
|
+
when String
|
|
84
|
+
content
|
|
85
|
+
else
|
|
86
|
+
raise ArgumentError, "Content must be Hash or String"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
File.write(path, file_content)
|
|
90
|
+
yield
|
|
91
|
+
ensure
|
|
92
|
+
FileUtils.rm_f(path) if path && File.exist?(path)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Execute block with temporary environment variables
|
|
96
|
+
def with_env(vars)
|
|
97
|
+
old_values = {}
|
|
98
|
+
|
|
99
|
+
vars.each do |key, value|
|
|
100
|
+
old_values[key] = ENV[key]
|
|
101
|
+
ENV[key] = value
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
yield
|
|
105
|
+
ensure
|
|
106
|
+
old_values.each do |key, value|
|
|
107
|
+
if value.nil?
|
|
108
|
+
ENV.delete(key)
|
|
109
|
+
else
|
|
110
|
+
ENV[key] = value
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Create multi-level config setup for any ace-* gem
|
|
116
|
+
def with_cascade_configs(gem_name = "core", configs = {})
|
|
117
|
+
paths = []
|
|
118
|
+
old_home = ENV["HOME"]
|
|
119
|
+
temp_home = nil
|
|
120
|
+
|
|
121
|
+
begin
|
|
122
|
+
# Create project config
|
|
123
|
+
if configs[:project]
|
|
124
|
+
project_path = "./.ace/#{gem_name}/config.yml"
|
|
125
|
+
FileUtils.mkdir_p(File.dirname(project_path))
|
|
126
|
+
File.write(project_path, configs[:project].to_yaml)
|
|
127
|
+
paths << project_path
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Create home config
|
|
131
|
+
if configs[:home]
|
|
132
|
+
temp_home = Dir.mktmpdir("ace-test-home")
|
|
133
|
+
ENV["HOME"] = temp_home
|
|
134
|
+
home_path = File.join(temp_home, ".ace", gem_name, "config.yml")
|
|
135
|
+
FileUtils.mkdir_p(File.dirname(home_path))
|
|
136
|
+
File.write(home_path, configs[:home].to_yaml)
|
|
137
|
+
paths << home_path
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
yield
|
|
141
|
+
ensure
|
|
142
|
+
ENV["HOME"] = old_home
|
|
143
|
+
FileUtils.rm_rf(temp_home) if temp_home && Dir.exist?(temp_home)
|
|
144
|
+
paths.each { |path| FileUtils.rm_f(path) if File.exist?(path) }
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Create sample config content
|
|
149
|
+
def sample_config(gem_name: "core", level: "default", custom: {})
|
|
150
|
+
base = {
|
|
151
|
+
"ace" => {
|
|
152
|
+
"level" => level,
|
|
153
|
+
gem_name => {
|
|
154
|
+
"version" => "1.0.0",
|
|
155
|
+
"environment" => "test"
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
deep_merge(base, custom)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Create .env file content
|
|
164
|
+
def sample_env_content(vars = {})
|
|
165
|
+
default_vars = {
|
|
166
|
+
"ACE_ENV" => "test",
|
|
167
|
+
"ACE_DEBUG" => "false"
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
default_vars.merge(vars).map do |key, value|
|
|
171
|
+
"#{key}=#{value}"
|
|
172
|
+
end.join("\n")
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Assert config has expected structure
|
|
176
|
+
def assert_config_structure(config, expected)
|
|
177
|
+
assert_kind_of Hash, config
|
|
178
|
+
|
|
179
|
+
expected.each do |key, value|
|
|
180
|
+
assert config.key?(key), "Config missing key: #{key}"
|
|
181
|
+
|
|
182
|
+
if value.is_a?(Hash)
|
|
183
|
+
assert_config_structure(config[key], value)
|
|
184
|
+
else
|
|
185
|
+
assert_equal value, config[key], "Config value mismatch for #{key}"
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Assert config cascade precedence
|
|
191
|
+
def assert_precedence(resolver, key_path, expected_value, source_description)
|
|
192
|
+
config = resolver.resolve
|
|
193
|
+
actual = config.get(*key_path.split("."))
|
|
194
|
+
|
|
195
|
+
assert_equal expected_value, actual,
|
|
196
|
+
"Expected #{key_path} to be '#{expected_value}' from #{source_description}, got '#{actual}'"
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Create malformed YAML content
|
|
200
|
+
def malformed_yaml
|
|
201
|
+
"ace:\n invalid: [\n unclosed"
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Create valid but complex YAML
|
|
205
|
+
def complex_yaml
|
|
206
|
+
{
|
|
207
|
+
"ace" => {
|
|
208
|
+
"arrays" => [1, 2, 3],
|
|
209
|
+
"nested" => {
|
|
210
|
+
"deep" => {
|
|
211
|
+
"value" => "found"
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
"symbols" => {
|
|
215
|
+
"key" => :symbol_value
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}.to_yaml
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
private
|
|
222
|
+
|
|
223
|
+
# Deep merge helper
|
|
224
|
+
def deep_merge(hash1, hash2)
|
|
225
|
+
hash1.merge(hash2) do |_key, old_val, new_val|
|
|
226
|
+
if old_val.is_a?(Hash) && new_val.is_a?(Hash)
|
|
227
|
+
deep_merge(old_val, new_val)
|
|
228
|
+
else
|
|
229
|
+
new_val
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|