Достижение: Добавлены скрипты и документация для релиза PDF Compressor.
- Добавлен release-body.md для подробных заметок о релизе на русском языке. - Реализован release-gitea.ps1 для автоматизированного релиза Gitea с помощью PowerShell. - Создан release-gitea.sh для автоматизированного релиза Gitea с помощью Bash. - Добавлен release.sh для сборки и маркировки релизов с поддержкой нескольких платформ. - Улучшен пользовательский интерфейс благодаря информативному логированию и обработке ошибок. - Добавлена поддержка переменных окружения и управления конфигурацией. - Добавлена функция создания архивов и загрузки ресурсов в Gitea.
This commit is contained in:
269
internal/domain/entities/app_config.go
Normal file
269
internal/domain/entities/app_config.go
Normal file
@@ -0,0 +1,269 @@
|
||||
package entities
|
||||
|
||||
import "time"
|
||||
|
||||
// Config представляет конфигурацию приложения
|
||||
type Config struct {
|
||||
Scanner ScannerConfig `yaml:"scanner"`
|
||||
Compression AppCompressionConfig `yaml:"compression"`
|
||||
Processing ProcessingConfig `yaml:"processing"`
|
||||
Output OutputConfig `yaml:"output"`
|
||||
}
|
||||
|
||||
// ScannerConfig настройки сканирования директорий
|
||||
type ScannerConfig struct {
|
||||
SourceDirectory string `yaml:"source_directory"`
|
||||
TargetDirectory string `yaml:"target_directory"`
|
||||
ReplaceOriginal bool `yaml:"replace_original"`
|
||||
}
|
||||
|
||||
// AppCompressionConfig настройки сжатия приложения
|
||||
type AppCompressionConfig struct {
|
||||
Level int `yaml:"level"`
|
||||
Algorithm string `yaml:"algorithm"`
|
||||
AutoStart bool `yaml:"auto_start"`
|
||||
UniPDFLicenseKey string `yaml:"unipdf_license_key"`
|
||||
// Настройки сжатия изображений
|
||||
EnableJPEG bool `yaml:"enable_jpeg"`
|
||||
EnablePNG bool `yaml:"enable_png"`
|
||||
JPEGQuality int `yaml:"jpeg_quality"` // Качество JPEG в процентах (10-50)
|
||||
PNGQuality int `yaml:"png_quality"` // Качество PNG в процентах (10-50)
|
||||
}
|
||||
|
||||
// ProcessingConfig настройки обработки
|
||||
type ProcessingConfig struct {
|
||||
ParallelWorkers int `yaml:"parallel_workers"`
|
||||
TimeoutSeconds int `yaml:"timeout_seconds"`
|
||||
RetryAttempts int `yaml:"retry_attempts"`
|
||||
}
|
||||
|
||||
// OutputConfig настройки вывода
|
||||
type OutputConfig struct {
|
||||
LogLevel string `yaml:"log_level"`
|
||||
ProgressBar bool `yaml:"progress_bar"`
|
||||
LogToFile bool `yaml:"log_to_file"`
|
||||
LogFileName string `yaml:"log_file_name"`
|
||||
LogMaxSizeMB int `yaml:"log_max_size_mb"`
|
||||
}
|
||||
|
||||
// ProcessingStatus статус обработки
|
||||
type ProcessingStatus struct {
|
||||
// Текущая фаза обработки
|
||||
Phase ProcessingPhase
|
||||
|
||||
// Информация о текущем файле
|
||||
CurrentFile string
|
||||
CurrentFileSize int64
|
||||
|
||||
// Общая статистика
|
||||
TotalFiles int
|
||||
ProcessedFiles int
|
||||
SuccessfulFiles int
|
||||
FailedFiles int
|
||||
SkippedFiles int
|
||||
|
||||
// Прогресс
|
||||
Progress float64
|
||||
|
||||
// Статистика сжатия
|
||||
TotalOriginalSize int64
|
||||
TotalCompressedSize int64
|
||||
TotalSavedSpace int64
|
||||
AverageCompression float64
|
||||
|
||||
// Текущий результат
|
||||
LastResult *CompressionResult
|
||||
|
||||
// Время выполнения
|
||||
StartTime time.Time
|
||||
ElapsedTime time.Duration
|
||||
EstimatedTime time.Duration
|
||||
|
||||
// Состояние
|
||||
IsComplete bool
|
||||
Error error
|
||||
|
||||
// Сообщение для UI
|
||||
Message string
|
||||
}
|
||||
|
||||
// ProcessingPhase фаза обработки
|
||||
type ProcessingPhase int
|
||||
|
||||
const (
|
||||
PhaseInitializing ProcessingPhase = iota
|
||||
PhaseScanning
|
||||
PhaseCompressing
|
||||
PhaseReplacing
|
||||
PhaseCompleted
|
||||
PhaseFailed
|
||||
)
|
||||
|
||||
// UIScreen типы экранов UI
|
||||
type UIScreen int
|
||||
|
||||
const (
|
||||
UIScreenMenu UIScreen = iota
|
||||
UIScreenConfig
|
||||
UIScreenProcessing
|
||||
// UIScreenResults
|
||||
)
|
||||
|
||||
// Validate проверяет корректность конфигурации приложения
|
||||
func (c *AppCompressionConfig) Validate() error {
|
||||
// Проверка уровня сжатия
|
||||
if c.Level < 10 || c.Level > 90 {
|
||||
return ErrInvalidCompressionLevel
|
||||
}
|
||||
|
||||
// Проверка качества JPEG
|
||||
if c.EnableJPEG {
|
||||
if c.JPEGQuality < 10 || c.JPEGQuality > 50 || c.JPEGQuality%5 != 0 {
|
||||
return ErrInvalidJPEGQuality
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка качества PNG
|
||||
if c.EnablePNG {
|
||||
if c.PNGQuality < 10 || c.PNGQuality > 50 || c.PNGQuality%5 != 0 {
|
||||
return ErrInvalidPNGQuality
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSupportedImageFormats возвращает список поддерживаемых форматов изображений
|
||||
func (c *AppCompressionConfig) GetSupportedImageFormats() []string {
|
||||
var formats []string
|
||||
if c.EnableJPEG {
|
||||
formats = append(formats, "JPEG")
|
||||
}
|
||||
if c.EnablePNG {
|
||||
formats = append(formats, "PNG")
|
||||
}
|
||||
return formats
|
||||
}
|
||||
|
||||
// NewProcessingStatus создает новый статус обработки
|
||||
func NewProcessingStatus(totalFiles int) *ProcessingStatus {
|
||||
return &ProcessingStatus{
|
||||
Phase: PhaseInitializing,
|
||||
TotalFiles: totalFiles,
|
||||
StartTime: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateProgress обновляет прогресс обработки
|
||||
func (ps *ProcessingStatus) UpdateProgress() {
|
||||
if ps.TotalFiles > 0 {
|
||||
ps.Progress = float64(ps.ProcessedFiles) / float64(ps.TotalFiles) * 100
|
||||
}
|
||||
|
||||
ps.ElapsedTime = time.Since(ps.StartTime)
|
||||
|
||||
// Оценка оставшегося времени
|
||||
if ps.ProcessedFiles > 0 && ps.ProcessedFiles < ps.TotalFiles {
|
||||
avgTimePerFile := ps.ElapsedTime / time.Duration(ps.ProcessedFiles)
|
||||
remainingFiles := ps.TotalFiles - ps.ProcessedFiles
|
||||
ps.EstimatedTime = avgTimePerFile * time.Duration(remainingFiles)
|
||||
}
|
||||
}
|
||||
|
||||
// AddResult добавляет результат обработки файла
|
||||
func (ps *ProcessingStatus) AddResult(result *CompressionResult) {
|
||||
ps.ProcessedFiles++
|
||||
ps.LastResult = result
|
||||
|
||||
if result.Success && result.Error == nil {
|
||||
ps.SuccessfulFiles++
|
||||
ps.TotalOriginalSize += result.OriginalSize
|
||||
ps.TotalCompressedSize += result.CompressedSize
|
||||
ps.TotalSavedSpace += result.SavedSpace
|
||||
|
||||
// Пересчитываем среднее сжатие
|
||||
if ps.TotalOriginalSize > 0 {
|
||||
ps.AverageCompression = ((float64(ps.TotalOriginalSize) - float64(ps.TotalCompressedSize)) / float64(ps.TotalOriginalSize)) * 100
|
||||
}
|
||||
} else {
|
||||
ps.FailedFiles++
|
||||
}
|
||||
|
||||
ps.UpdateProgress()
|
||||
}
|
||||
|
||||
// SetPhase устанавливает фазу обработки
|
||||
func (ps *ProcessingStatus) SetPhase(phase ProcessingPhase, message string) {
|
||||
ps.Phase = phase
|
||||
ps.Message = message
|
||||
}
|
||||
|
||||
// SetCurrentFile устанавлиет текущий обрабатываемый файл
|
||||
func (ps *ProcessingStatus) SetCurrentFile(filePath string, size int64) {
|
||||
ps.CurrentFile = filePath
|
||||
ps.CurrentFileSize = size
|
||||
}
|
||||
|
||||
// Complete завершает обработку
|
||||
func (ps *ProcessingStatus) Complete() {
|
||||
ps.IsComplete = true
|
||||
ps.Phase = PhaseCompleted
|
||||
ps.Progress = 100
|
||||
ps.ElapsedTime = time.Since(ps.StartTime)
|
||||
ps.EstimatedTime = 0
|
||||
}
|
||||
|
||||
// Fail отмечает обработку как неудачную
|
||||
func (ps *ProcessingStatus) Fail(err error) {
|
||||
ps.IsComplete = true
|
||||
ps.Phase = PhaseFailed
|
||||
ps.Error = err
|
||||
ps.ElapsedTime = time.Since(ps.StartTime)
|
||||
}
|
||||
|
||||
// GetPhaseName возвращает название фазы
|
||||
func (phase ProcessingPhase) String() string {
|
||||
switch phase {
|
||||
case PhaseInitializing:
|
||||
return "Инициализация"
|
||||
case PhaseScanning:
|
||||
return "Сканирование файлов"
|
||||
case PhaseCompressing:
|
||||
return "Сжатие файлов"
|
||||
case PhaseReplacing:
|
||||
return "Замена оригиналов"
|
||||
case PhaseCompleted:
|
||||
return "Завершено"
|
||||
case PhaseFailed:
|
||||
return "Ошибка"
|
||||
default:
|
||||
return "Неизвестно"
|
||||
}
|
||||
}
|
||||
|
||||
// FormatElapsedTime форматирует время выполнения
|
||||
func (ps *ProcessingStatus) FormatElapsedTime() string {
|
||||
duration := ps.ElapsedTime
|
||||
if duration < time.Second {
|
||||
return "< 1 сек"
|
||||
}
|
||||
if duration < time.Minute {
|
||||
return duration.Round(time.Second).String()
|
||||
}
|
||||
return duration.Round(time.Second).String()
|
||||
}
|
||||
|
||||
// FormatEstimatedTime форматирует оставшееся время
|
||||
func (ps *ProcessingStatus) FormatEstimatedTime() string {
|
||||
if ps.EstimatedTime == 0 {
|
||||
return "N/A"
|
||||
}
|
||||
duration := ps.EstimatedTime
|
||||
if duration < time.Second {
|
||||
return "< 1 сек"
|
||||
}
|
||||
if duration < time.Minute {
|
||||
return duration.Round(time.Second).String()
|
||||
}
|
||||
return duration.Round(time.Second).String()
|
||||
}
|
||||
88
internal/domain/entities/config.go
Normal file
88
internal/domain/entities/config.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package entities
|
||||
|
||||
// CompressionConfig представляет конфигурацию сжатия
|
||||
type CompressionConfig struct {
|
||||
Level int // Уровень сжатия (10-90)
|
||||
ImageQuality int // Качество изображений (10-100)
|
||||
ImageCompression bool // Сжимать изображения
|
||||
RemoveDuplicates bool // Удалять дубликаты объектов
|
||||
CompressStreams bool // Сжимать потоки данных
|
||||
RemoveMetadata bool // Удалять метаданные
|
||||
RemoveAnnotations bool // Удалять аннотации
|
||||
RemoveAttachments bool // Удалять вложения
|
||||
OptimizeForWeb bool // Оптимизировать для веб
|
||||
UniPDFLicenseKey string // Лицензионный ключ для UniPDF
|
||||
}
|
||||
|
||||
// NewCompressionConfig создает конфигурацию сжатия на основе уровня
|
||||
func NewCompressionConfig(level int) *CompressionConfig {
|
||||
return NewCompressionConfigWithLicense(level, "")
|
||||
}
|
||||
|
||||
// NewCompressionConfigWithLicense создает конфигурацию сжатия с лицензионным ключом
|
||||
func NewCompressionConfigWithLicense(level int, licenseKey string) *CompressionConfig {
|
||||
if level < 10 {
|
||||
level = 10
|
||||
}
|
||||
if level > 90 {
|
||||
level = 90
|
||||
}
|
||||
|
||||
config := &CompressionConfig{
|
||||
Level: level,
|
||||
RemoveDuplicates: true,
|
||||
CompressStreams: true,
|
||||
OptimizeForWeb: true,
|
||||
UniPDFLicenseKey: licenseKey,
|
||||
}
|
||||
|
||||
switch {
|
||||
case level <= 20: // Слабое сжатие
|
||||
config.ImageQuality = 90
|
||||
config.ImageCompression = true
|
||||
config.RemoveMetadata = false
|
||||
config.RemoveAnnotations = false
|
||||
config.RemoveAttachments = false
|
||||
|
||||
case level <= 40: // Умеренное сжатие
|
||||
config.ImageQuality = 75
|
||||
config.ImageCompression = true
|
||||
config.RemoveMetadata = true
|
||||
config.RemoveAnnotations = false
|
||||
config.RemoveAttachments = false
|
||||
|
||||
case level <= 60: // Среднее сжатие
|
||||
config.ImageQuality = 60
|
||||
config.ImageCompression = true
|
||||
config.RemoveMetadata = true
|
||||
config.RemoveAnnotations = true
|
||||
config.RemoveAttachments = false
|
||||
|
||||
case level <= 80: // Высокое сжатие
|
||||
config.ImageQuality = 40
|
||||
config.ImageCompression = true
|
||||
config.RemoveMetadata = true
|
||||
config.RemoveAnnotations = true
|
||||
config.RemoveAttachments = true
|
||||
|
||||
default: // Максимальное сжатие (81-90%)
|
||||
config.ImageQuality = 25
|
||||
config.ImageCompression = true
|
||||
config.RemoveMetadata = true
|
||||
config.RemoveAnnotations = true
|
||||
config.RemoveAttachments = true
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// Validate проверяет корректность конфигурации
|
||||
func (c *CompressionConfig) Validate() error {
|
||||
if c.Level < 10 || c.Level > 90 {
|
||||
return ErrInvalidCompressionLevel
|
||||
}
|
||||
if c.ImageQuality < 10 || c.ImageQuality > 100 {
|
||||
return ErrInvalidImageQuality
|
||||
}
|
||||
return nil
|
||||
}
|
||||
127
internal/domain/entities/config_test.go
Normal file
127
internal/domain/entities/config_test.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package entities_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"compressor/internal/domain/entities"
|
||||
)
|
||||
|
||||
func TestNewCompressionConfig(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
level int
|
||||
expectedLevel int
|
||||
}{
|
||||
{"Normal level", 50, 50},
|
||||
{"Too low level", 5, 10},
|
||||
{"Too high level", 95, 90},
|
||||
{"Minimum level", 10, 10},
|
||||
{"Maximum level", 90, 90},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
config := entities.NewCompressionConfig(tt.level)
|
||||
if config.Level != tt.expectedLevel {
|
||||
t.Errorf("Expected level %d, got %d", tt.expectedLevel, config.Level)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompressionConfig_Validate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config *entities.CompressionConfig
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid config",
|
||||
config: &entities.CompressionConfig{
|
||||
Level: 50,
|
||||
ImageQuality: 75,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid compression level - too low",
|
||||
config: &entities.CompressionConfig{
|
||||
Level: 5,
|
||||
ImageQuality: 75,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid compression level - too high",
|
||||
config: &entities.CompressionConfig{
|
||||
Level: 95,
|
||||
ImageQuality: 75,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid image quality - too low",
|
||||
config: &entities.CompressionConfig{
|
||||
Level: 50,
|
||||
ImageQuality: 5,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid image quality - too high",
|
||||
config: &entities.CompressionConfig{
|
||||
Level: 50,
|
||||
ImageQuality: 105,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.config.Validate()
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Validate() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompressionConfigLevels(t *testing.T) {
|
||||
tests := []struct {
|
||||
level int
|
||||
expectedImageQuality int
|
||||
expectedMetadata bool
|
||||
expectedAnnotations bool
|
||||
expectedAttachments bool
|
||||
}{
|
||||
{15, 90, false, false, false}, // Слабое сжатие
|
||||
{30, 75, true, false, false}, // Умеренное сжатие
|
||||
{50, 60, true, true, false}, // Среднее сжатие
|
||||
{70, 40, true, true, true}, // Высокое сжатие
|
||||
{85, 25, true, true, true}, // Максимальное сжатие
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(fmt.Sprintf("Level %d", tt.level), func(t *testing.T) {
|
||||
config := entities.NewCompressionConfig(tt.level)
|
||||
|
||||
if config.ImageQuality != tt.expectedImageQuality {
|
||||
t.Errorf("Expected ImageQuality %d, got %d", tt.expectedImageQuality, config.ImageQuality)
|
||||
}
|
||||
|
||||
if config.RemoveMetadata != tt.expectedMetadata {
|
||||
t.Errorf("Expected RemoveMetadata %v, got %v", tt.expectedMetadata, config.RemoveMetadata)
|
||||
}
|
||||
|
||||
if config.RemoveAnnotations != tt.expectedAnnotations {
|
||||
t.Errorf("Expected RemoveAnnotations %v, got %v", tt.expectedAnnotations, config.RemoveAnnotations)
|
||||
}
|
||||
|
||||
if config.RemoveAttachments != tt.expectedAttachments {
|
||||
t.Errorf("Expected RemoveAttachments %v, got %v", tt.expectedAttachments, config.RemoveAttachments)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
16
internal/domain/entities/errors.go
Normal file
16
internal/domain/entities/errors.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package entities
|
||||
|
||||
import "errors"
|
||||
|
||||
// Доменные ошибки
|
||||
var (
|
||||
ErrInvalidCompressionLevel = errors.New("уровень сжатия должен быть от 10 до 90")
|
||||
ErrInvalidImageQuality = errors.New("качество изображения должно быть от 10 до 100")
|
||||
ErrInvalidJPEGQuality = errors.New("качество JPEG должно быть от 10 до 50 с шагом 5")
|
||||
ErrInvalidPNGQuality = errors.New("качество PNG должно быть от 10 до 50 с шагом 5")
|
||||
ErrFileNotFound = errors.New("файл не найден")
|
||||
ErrInvalidFileFormat = errors.New("неверный формат файла")
|
||||
ErrCompressionFailed = errors.New("ошибка сжатия файла")
|
||||
ErrDirectoryNotFound = errors.New("директория не найдена")
|
||||
ErrNoFilesFound = errors.New("PDF файлы не найдены")
|
||||
)
|
||||
37
internal/domain/entities/pdf.go
Normal file
37
internal/domain/entities/pdf.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package entities
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// PDFDocument представляет PDF документ
|
||||
type PDFDocument struct {
|
||||
Path string
|
||||
Size int64
|
||||
ModifiedTime time.Time
|
||||
Pages int
|
||||
}
|
||||
|
||||
// CompressionResult представляет результат сжатия
|
||||
type CompressionResult struct {
|
||||
CurrentFile string
|
||||
OriginalSize int64
|
||||
CompressedSize int64
|
||||
CompressionRatio float64
|
||||
SavedSpace int64
|
||||
Success bool
|
||||
Error error
|
||||
}
|
||||
|
||||
// CalculateCompressionRatio вычисляет коэффициент сжатия
|
||||
func (cr *CompressionResult) CalculateCompressionRatio() {
|
||||
if cr.OriginalSize > 0 {
|
||||
cr.CompressionRatio = ((float64(cr.OriginalSize) - float64(cr.CompressedSize)) / float64(cr.OriginalSize)) * 100
|
||||
cr.SavedSpace = cr.OriginalSize - cr.CompressedSize
|
||||
}
|
||||
}
|
||||
|
||||
// IsEffective проверяет, было ли сжатие эффективным
|
||||
func (cr *CompressionResult) IsEffective() bool {
|
||||
return cr.Success && cr.CompressionRatio > 0
|
||||
}
|
||||
112
internal/domain/entities/pdf_test.go
Normal file
112
internal/domain/entities/pdf_test.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package entities_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"compressor/internal/domain/entities"
|
||||
)
|
||||
|
||||
func TestCompressionResult_CalculateCompressionRatio(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
originalSize int64
|
||||
compressedSize int64
|
||||
expectedRatio float64
|
||||
expectedSavedSpace int64
|
||||
}{
|
||||
{
|
||||
name: "50% compression",
|
||||
originalSize: 1000,
|
||||
compressedSize: 500,
|
||||
expectedRatio: 50.0,
|
||||
expectedSavedSpace: 500,
|
||||
},
|
||||
{
|
||||
name: "25% compression",
|
||||
originalSize: 1000,
|
||||
compressedSize: 750,
|
||||
expectedRatio: 25.0,
|
||||
expectedSavedSpace: 250,
|
||||
},
|
||||
{
|
||||
name: "No compression",
|
||||
originalSize: 1000,
|
||||
compressedSize: 1000,
|
||||
expectedRatio: 0.0,
|
||||
expectedSavedSpace: 0,
|
||||
},
|
||||
{
|
||||
name: "File got bigger",
|
||||
originalSize: 1000,
|
||||
compressedSize: 1100,
|
||||
expectedRatio: -10.0,
|
||||
expectedSavedSpace: -100,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := &entities.CompressionResult{
|
||||
OriginalSize: tt.originalSize,
|
||||
CompressedSize: tt.compressedSize,
|
||||
}
|
||||
|
||||
result.CalculateCompressionRatio()
|
||||
|
||||
if result.CompressionRatio != tt.expectedRatio {
|
||||
t.Errorf("Expected compression ratio %f, got %f", tt.expectedRatio, result.CompressionRatio)
|
||||
}
|
||||
|
||||
if result.SavedSpace != tt.expectedSavedSpace {
|
||||
t.Errorf("Expected saved space %d, got %d", tt.expectedSavedSpace, result.SavedSpace)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompressionResult_IsEffective(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
result *entities.CompressionResult
|
||||
expectedEffective bool
|
||||
}{
|
||||
{
|
||||
name: "Effective compression",
|
||||
result: &entities.CompressionResult{
|
||||
OriginalSize: 1000,
|
||||
CompressedSize: 500,
|
||||
CompressionRatio: 50.0,
|
||||
Success: true,
|
||||
},
|
||||
expectedEffective: true,
|
||||
},
|
||||
{
|
||||
name: "No compression but successful",
|
||||
result: &entities.CompressionResult{
|
||||
OriginalSize: 1000,
|
||||
CompressedSize: 1000,
|
||||
CompressionRatio: 0.0,
|
||||
Success: true,
|
||||
},
|
||||
expectedEffective: false,
|
||||
},
|
||||
{
|
||||
name: "Good compression but failed",
|
||||
result: &entities.CompressionResult{
|
||||
OriginalSize: 1000,
|
||||
CompressedSize: 500,
|
||||
CompressionRatio: 50.0,
|
||||
Success: false,
|
||||
},
|
||||
expectedEffective: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.result.IsEffective(); got != tt.expectedEffective {
|
||||
t.Errorf("IsEffective() = %v, want %v", got, tt.expectedEffective)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
9
internal/domain/repositories/app_config_repository.go
Normal file
9
internal/domain/repositories/app_config_repository.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package repositories
|
||||
|
||||
import "compressor/internal/domain/entities"
|
||||
|
||||
// ConfigRepository интерфейс для работы с конфигурацией приложения
|
||||
type AppConfigRepository interface {
|
||||
Load(configPath string) (*entities.Config, error)
|
||||
Save(configPath string, config *entities.Config) error
|
||||
}
|
||||
24
internal/domain/repositories/interfaces.go
Normal file
24
internal/domain/repositories/interfaces.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package repositories
|
||||
|
||||
import (
|
||||
"compressor/internal/domain/entities"
|
||||
)
|
||||
|
||||
// PDFCompressor интерфейс для сжатия PDF файлов
|
||||
type PDFCompressor interface {
|
||||
Compress(inputPath, outputPath string, config *entities.CompressionConfig) (*entities.CompressionResult, error)
|
||||
}
|
||||
|
||||
// FileRepository интерфейс для работы с файловой системой
|
||||
type FileRepository interface {
|
||||
GetFileInfo(path string) (*entities.PDFDocument, error)
|
||||
FileExists(path string) bool
|
||||
CreateDirectory(path string) error
|
||||
ListPDFFiles(directory string) ([]string, error)
|
||||
}
|
||||
|
||||
// ConfigRepository интерфейс для работы с конфигурацией
|
||||
type ConfigRepository interface {
|
||||
GetCompressionConfig(level int) (*entities.CompressionConfig, error)
|
||||
ValidateConfig(config *entities.CompressionConfig) error
|
||||
}
|
||||
11
internal/domain/repositories/logger.go
Normal file
11
internal/domain/repositories/logger.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package repositories
|
||||
|
||||
// Logger интерфейс для логирования
|
||||
type Logger interface {
|
||||
Debug(format string, args ...interface{})
|
||||
Info(format string, args ...interface{})
|
||||
Warning(format string, args ...interface{})
|
||||
Error(format string, args ...interface{})
|
||||
Success(format string, args ...interface{})
|
||||
Close() error
|
||||
}
|
||||
Reference in New Issue
Block a user