Files
Gnuboard7/tests/Unit/Extension/ChangelogParserTest.php
2026-04-01 10:30:52 +09:00

501 lines
13 KiB
PHP

<?php
namespace Tests\Unit\Extension;
use App\Extension\Helpers\ChangelogParser;
use Illuminate\Support\Facades\File;
use Tests\TestCase;
class ChangelogParserTest extends TestCase
{
private string $testDir;
protected function setUp(): void
{
parent::setUp();
$this->testDir = base_path('tests/_temp_changelog_test');
File::ensureDirectoryExists($this->testDir);
}
protected function tearDown(): void
{
if (File::isDirectory($this->testDir)) {
File::deleteDirectory($this->testDir);
}
parent::tearDown();
}
/**
* 표준 Keep a Changelog 형식을 파싱합니다.
*/
public function test_parse_standard_format(): void
{
$content = <<<'MD'
# Changelog
## [0.1.2] - 2026-02-25
### Added
- 새 기능 A
- 새 기능 B
### Fixed
- 버그 C 수정
## [0.1.1] - 2026-02-20
### Changed
- 기존 기능 D 개선
MD;
$filePath = $this->testDir.'/CHANGELOG.md';
File::put($filePath, $content);
$result = ChangelogParser::parse($filePath);
$this->assertCount(2, $result);
// 첫 번째 버전 (0.1.2)
$this->assertSame('0.1.2', $result[0]['version']);
$this->assertSame('2026-02-25', $result[0]['date']);
$this->assertCount(2, $result[0]['categories']);
$this->assertSame('Added', $result[0]['categories'][0]['name']);
$this->assertSame(['새 기능 A', '새 기능 B'], $result[0]['categories'][0]['items']);
$this->assertSame('Fixed', $result[0]['categories'][1]['name']);
$this->assertSame(['버그 C 수정'], $result[0]['categories'][1]['items']);
// 두 번째 버전 (0.1.1)
$this->assertSame('0.1.1', $result[1]['version']);
$this->assertSame('2026-02-20', $result[1]['date']);
$this->assertCount(1, $result[1]['categories']);
$this->assertSame('Changed', $result[1]['categories'][0]['name']);
$this->assertSame(['기존 기능 D 개선'], $result[1]['categories'][0]['items']);
}
/**
* 대괄호 없는 버전 형식도 파싱합니다.
*/
public function test_parse_version_without_brackets(): void
{
$content = <<<'MD'
# Changelog
## 1.0.0 - 2026-01-01
### Added
- 초기 릴리스
MD;
$filePath = $this->testDir.'/CHANGELOG.md';
File::put($filePath, $content);
$result = ChangelogParser::parse($filePath);
$this->assertCount(1, $result);
$this->assertSame('1.0.0', $result[0]['version']);
$this->assertSame('2026-01-01', $result[0]['date']);
}
/**
* 날짜가 없는 버전도 파싱합니다.
*/
public function test_parse_version_without_date(): void
{
$content = <<<'MD'
# Changelog
## [0.1.0]
### Added
- 초기 기능
MD;
$filePath = $this->testDir.'/CHANGELOG.md';
File::put($filePath, $content);
$result = ChangelogParser::parse($filePath);
$this->assertCount(1, $result);
$this->assertSame('0.1.0', $result[0]['version']);
$this->assertNull($result[0]['date']);
}
/**
* 파일이 존재하지 않으면 빈 배열을 반환합니다.
*/
public function test_parse_returns_empty_for_nonexistent_file(): void
{
$result = ChangelogParser::parse($this->testDir.'/nonexistent.md');
$this->assertSame([], $result);
}
/**
* 빈 파일은 빈 배열을 반환합니다.
*/
public function test_parse_returns_empty_for_empty_file(): void
{
$filePath = $this->testDir.'/CHANGELOG.md';
File::put($filePath, '');
$result = ChangelogParser::parse($filePath);
$this->assertSame([], $result);
}
/**
* 버전 헤더가 없는 파일은 빈 배열을 반환합니다.
*/
public function test_parse_returns_empty_for_no_version_headers(): void
{
$content = <<<'MD'
# Changelog
이 프로젝트의 모든 변경사항을 기록합니다.
MD;
$filePath = $this->testDir.'/CHANGELOG.md';
File::put($filePath, $content);
$result = ChangelogParser::parse($filePath);
$this->assertSame([], $result);
}
/**
* 여러 카테고리를 올바르게 파싱합니다.
*/
public function test_parse_multiple_categories(): void
{
$content = <<<'MD'
# Changelog
## [0.2.0] - 2026-03-01
### Added
- 새 기능
### Changed
- 변경 사항
### Deprecated
- 더 이상 사용하지 않는 기능
### Removed
- 제거된 기능
### Fixed
- 수정된 버그
### Security
- 보안 패치
MD;
$filePath = $this->testDir.'/CHANGELOG.md';
File::put($filePath, $content);
$result = ChangelogParser::parse($filePath);
$this->assertCount(1, $result);
$this->assertCount(6, $result[0]['categories']);
$this->assertSame('Added', $result[0]['categories'][0]['name']);
$this->assertSame('Changed', $result[0]['categories'][1]['name']);
$this->assertSame('Deprecated', $result[0]['categories'][2]['name']);
$this->assertSame('Removed', $result[0]['categories'][3]['name']);
$this->assertSame('Fixed', $result[0]['categories'][4]['name']);
$this->assertSame('Security', $result[0]['categories'][5]['name']);
}
/**
* 버전 범위 필터링이 올바르게 동작합니다 (from 초과, to 이하).
*/
public function test_get_version_range(): void
{
$content = <<<'MD'
# Changelog
## [0.1.3] - 2026-02-28
### Added
- 기능 C
## [0.1.2] - 2026-02-25
### Added
- 기능 B
## [0.1.1] - 2026-02-20
### Added
- 기능 A
## [0.1.0] - 2026-02-15
### Added
- 초기 릴리스
MD;
$filePath = $this->testDir.'/CHANGELOG.md';
File::put($filePath, $content);
// from=0.1.1 초과, to=0.1.3 이하 → 0.1.2, 0.1.3
$result = ChangelogParser::getVersionRange($filePath, '0.1.1', '0.1.3');
$this->assertCount(2, $result);
$this->assertSame('0.1.3', $result[0]['version']);
$this->assertSame('0.1.2', $result[1]['version']);
}
/**
* 버전 범위에 해당하는 엔트리가 없으면 빈 배열을 반환합니다.
*/
public function test_get_version_range_returns_empty_for_no_match(): void
{
$content = <<<'MD'
# Changelog
## [0.1.0] - 2026-02-15
### Added
- 초기 릴리스
MD;
$filePath = $this->testDir.'/CHANGELOG.md';
File::put($filePath, $content);
$result = ChangelogParser::getVersionRange($filePath, '0.1.0', '0.1.0');
$this->assertSame([], $result);
}
/**
* 파일 미존재 시 버전 범위도 빈 배열을 반환합니다.
*/
public function test_get_version_range_returns_empty_for_nonexistent_file(): void
{
$result = ChangelogParser::getVersionRange($this->testDir.'/nonexistent.md', '0.1.0', '0.1.1');
$this->assertSame([], $result);
}
/**
* active 소스에서 CHANGELOG.md 경로를 올바르게 결정합니다.
*/
public function test_resolve_changelog_path_active_source(): void
{
$basePath = $this->testDir;
$identifier = 'test-module';
File::ensureDirectoryExists($basePath.'/'.$identifier);
File::put($basePath.'/'.$identifier.'/CHANGELOG.md', '# Changelog');
$result = ChangelogParser::resolveChangelogPath($basePath, $identifier);
$this->assertNotNull($result);
$this->assertStringContainsString($identifier.DIRECTORY_SEPARATOR.'CHANGELOG.md', $result);
}
/**
* bundled 소스에서 CHANGELOG.md 경로를 올바르게 결정합니다.
*/
public function test_resolve_changelog_path_bundled_source(): void
{
$basePath = $this->testDir;
$identifier = 'test-module';
File::ensureDirectoryExists($basePath.'/_bundled/'.$identifier);
File::put($basePath.'/_bundled/'.$identifier.'/CHANGELOG.md', '# Changelog');
$result = ChangelogParser::resolveChangelogPath($basePath, $identifier, 'bundled');
$this->assertNotNull($result);
$this->assertStringContainsString('_bundled'.DIRECTORY_SEPARATOR.$identifier.DIRECTORY_SEPARATOR.'CHANGELOG.md', $result);
}
/**
* 파일이 존재하지 않으면 null을 반환합니다.
*/
public function test_resolve_changelog_path_returns_null_for_missing_file(): void
{
$result = ChangelogParser::resolveChangelogPath($this->testDir, 'nonexistent-module');
$this->assertNull($result);
}
/**
* source가 null이면 active로 동작합니다.
*/
public function test_resolve_changelog_path_defaults_to_active(): void
{
$basePath = $this->testDir;
$identifier = 'test-module';
File::ensureDirectoryExists($basePath.'/'.$identifier);
File::put($basePath.'/'.$identifier.'/CHANGELOG.md', '# Changelog');
$result = ChangelogParser::resolveChangelogPath($basePath, $identifier, null);
$this->assertNotNull($result);
$this->assertStringContainsString($identifier.DIRECTORY_SEPARATOR.'CHANGELOG.md', $result);
}
/**
* active에 파일이 없으면 _bundled로 폴백합니다.
*/
public function test_resolve_changelog_path_fallback_to_bundled(): void
{
$basePath = $this->testDir;
$identifier = 'test-module';
// active 디렉토리에는 CHANGELOG.md 없음
// _bundled에만 존재
File::ensureDirectoryExists($basePath.'/_bundled/'.$identifier);
File::put($basePath.'/_bundled/'.$identifier.'/CHANGELOG.md', '# Changelog');
$result = ChangelogParser::resolveChangelogPath($basePath, $identifier);
$this->assertNotNull($result);
$this->assertStringContainsString('_bundled'.DIRECTORY_SEPARATOR.$identifier.DIRECTORY_SEPARATOR.'CHANGELOG.md', $result);
}
/**
* active와 _bundled 모두에 파일이 있으면 active를 우선합니다.
*/
public function test_resolve_changelog_path_active_takes_priority_over_bundled(): void
{
$basePath = $this->testDir;
$identifier = 'test-module';
File::ensureDirectoryExists($basePath.'/'.$identifier);
File::put($basePath.'/'.$identifier.'/CHANGELOG.md', '# Active Changelog');
File::ensureDirectoryExists($basePath.'/_bundled/'.$identifier);
File::put($basePath.'/_bundled/'.$identifier.'/CHANGELOG.md', '# Bundled Changelog');
$result = ChangelogParser::resolveChangelogPath($basePath, $identifier);
$this->assertNotNull($result);
// active가 우선이므로 _bundled가 포함되지 않아야 함
$this->assertStringNotContainsString('_bundled', $result);
}
/**
* 프리릴리스 버전(7.0.0-alpha.1)을 올바르게 파싱합니다.
*/
public function test_parse_prerelease_version(): void
{
$content = <<<'MD'
# Changelog
## [7.0.0-beta.1] - 2026-03-07
### Added
- 코어 업데이트 시스템
### Fixed
- 확장 의존성 체크 오류 수정
## [7.0.0-alpha.2] - 2026-02-15
### Added
- 초기 알파 기능
MD;
$filePath = $this->testDir.'/CHANGELOG.md';
File::put($filePath, $content);
$result = ChangelogParser::parse($filePath);
$this->assertCount(2, $result);
// 7.0.0-beta.1
$this->assertSame('7.0.0-beta.1', $result[0]['version']);
$this->assertSame('2026-03-07', $result[0]['date']);
$this->assertCount(2, $result[0]['categories']);
// 7.0.0-alpha.2
$this->assertSame('7.0.0-alpha.2', $result[1]['version']);
$this->assertSame('2026-02-15', $result[1]['date']);
}
/**
* 대괄호 없는 프리릴리스 버전도 파싱합니다.
*/
public function test_parse_prerelease_version_without_brackets(): void
{
$content = <<<'MD'
# Changelog
## 7.0.0-rc.1 - 2026-04-01
### Added
- RC 기능
MD;
$filePath = $this->testDir.'/CHANGELOG.md';
File::put($filePath, $content);
$result = ChangelogParser::parse($filePath);
$this->assertCount(1, $result);
$this->assertSame('7.0.0-rc.1', $result[0]['version']);
$this->assertSame('2026-04-01', $result[0]['date']);
}
/**
* 프리릴리스 버전 범위 필터링이 올바르게 동작합니다.
*/
public function test_get_version_range_with_prerelease(): void
{
$content = <<<'MD'
# Changelog
## [7.0.0-beta.2] - 2026-03-15
### Added
- 베타2 기능
## [7.0.0-beta.1] - 2026-03-07
### Added
- 베타1 기능
## [7.0.0-alpha.1] - 2026-02-01
### Added
- 알파1 기능
MD;
$filePath = $this->testDir.'/CHANGELOG.md';
File::put($filePath, $content);
// alpha.1 초과 ~ beta.2 이하
$result = ChangelogParser::getVersionRange($filePath, '7.0.0-alpha.1', '7.0.0-beta.2');
$this->assertCount(2, $result);
$this->assertSame('7.0.0-beta.2', $result[0]['version']);
$this->assertSame('7.0.0-beta.1', $result[1]['version']);
}
/**
* bundled 소스 지정 시에는 폴백하지 않습니다.
*/
public function test_resolve_changelog_path_bundled_source_no_fallback(): void
{
$basePath = $this->testDir;
$identifier = 'test-module';
// active에만 존재, _bundled에는 없음
File::ensureDirectoryExists($basePath.'/'.$identifier);
File::put($basePath.'/'.$identifier.'/CHANGELOG.md', '# Changelog');
$result = ChangelogParser::resolveChangelogPath($basePath, $identifier, 'bundled');
$this->assertNull($result);
}
}