Export and Import of Game system master data

This commit is contained in:
2026-01-04 23:06:40 +01:00
parent 9dcd8121f1
commit 2dcb4e00fa
5 changed files with 3075 additions and 45 deletions
+216
View File
@@ -0,0 +1,216 @@
# Master Data Export/Import
## Overview
The export/import mechanism allows exporting all master data from the `model_gsmaster` and `model_learning_costs` modules to JSON files and importing them back. The exported data is ID-independent, using natural keys (name + game_system) to identify records, making it suitable for:
- Migrating data between environments
- Version controlling master data
- Manually editing game data
- Sharing/distributing game systems
## Supported Entities
### From `model_learning_costs.go`:
- **Sources** (`learning_sources`) - Game books and source materials
- **SkillCategories** (`learning_skill_categories`) - Skill classification categories
- **SkillDifficulties** (`learning_skill_difficulties`) - Difficulty levels
- **SkillCategoryDifficulties** (`learning_skill_category_difficulties`) - Relationship between skills, categories, and difficulties with learning costs
### From `model_gsmaster.go`:
- **Skills** (`gsm_skills`) - Character skills
- **Spells** (`gsm_spells`) - Magic spells
## Excluded Entities
The following are NOT exported/imported:
- `AuditLogEntry` - Audit logs (transient data)
- `BamortBase` - Base character data (character-specific)
- `BamortCharTrait` - Character traits (character-specific)
- `Magisch` - Character magic data (character-specific)
- `LookupList` - System lookup lists (system config)
## File Format
All data is exported to JSON files with indentation for easy reading and editing:
```json
[
{
"name": "Skill Name",
"game_system": "midgard",
"source_code": "KOD",
...
}
]
```
## Natural Keys
Instead of database IDs, the following natural keys are used to identify records:
| Entity | Natural Key |
|--------|------------|
| Source | `code` |
| Skill | `name` + `game_system` |
| Spell | `name` + `game_system` |
| SkillCategory | `name` + `game_system` |
| SkillDifficulty | `name` + `game_system` |
| SkillCategoryDifficulty | `skill_name` + `skill_system` + `category_name` + `category_system` + `difficulty_name` + `difficulty_system` |
## Usage
### Export
```go
import "bamort/gsmaster"
// Export specific entity types
err := gsmaster.ExportSources("/path/to/output")
err := gsmaster.ExportSkills("/path/to/output")
err := gsmaster.ExportSpells("/path/to/output")
err := gsmaster.ExportSkillCategories("/path/to/output")
err := gsmaster.ExportSkillDifficulties("/path/to/output")
err := gsmaster.ExportSkillCategoryDifficulties("/path/to/output")
// Export all master data at once
err := gsmaster.ExportAll("/path/to/output")
```
### Import
```go
import "bamort/gsmaster"
// Import specific entity types
err := gsmaster.ImportSources("/path/to/input")
err := gsmaster.ImportSkills("/path/to/input")
err := gsmaster.ImportSpells("/path/to/input")
err := gsmaster.ImportSkillCategories("/path/to/input")
err := gsmaster.ImportSkillDifficulties("/path/to/input")
err := gsmaster.ImportSkillCategoryDifficulties("/path/to/input")
// Import all master data at once
err := gsmaster.ImportAll("/path/to/input")
```
## Import Behavior
The import mechanism follows an "upsert" pattern:
1. **Check if record exists** using natural keys
2. If **not found**: Create new record
3. If **found**: Update existing record with imported values
This allows for:
- Importing new data
- Updating existing data
- Safe re-import of previously exported data
## Dependency Order
When using `ExportAll()` and `ImportAll()`, entities are processed in dependency order:
**Export/Import Order:**
1. Sources (no dependencies)
2. SkillCategories (depends on Sources)
3. SkillDifficulties (no dependencies)
4. Skills (depends on Sources)
5. SkillCategoryDifficulties (depends on Skills, Categories, Difficulties)
6. Spells (depends on Sources)
## File Names
Each entity type is exported to its own file:
| Entity | Filename |
|--------|----------|
| Sources | `sources.json` |
| Skills | `skills.json` |
| Spells | `spells.json` |
| SkillCategories | `skill_categories.json` |
| SkillDifficulties | `skill_difficulties.json` |
| SkillCategoryDifficulties | `skill_category_difficulties.json` |
## Example Workflow
### Exporting Data
```go
package main
import (
"bamort/gsmaster"
"log"
)
func main() {
outputDir := "./exported_data"
if err := gsmaster.ExportAll(outputDir); err != nil {
log.Fatalf("Export failed: %v", err)
}
log.Println("All master data exported to", outputDir)
}
```
### Editing Exported Data
```bash
# Edit the JSON files manually
vim exported_data/skills.json
```
### Importing Modified Data
```go
package main
import (
"bamort/gsmaster"
"bamort/database"
"log"
)
func main() {
// Initialize database connection
database.InitDB()
inputDir := "./exported_data"
if err := gsmaster.ImportAll(inputDir); err != nil {
log.Fatalf("Import failed: %v", err)
}
log.Println("All master data imported from", inputDir)
}
```
## Error Handling
All export/import functions return errors that should be checked:
- **Export errors**: Usually file system issues (permissions, disk space)
- **Import errors**: Can be:
- File not found
- Invalid JSON format
- Missing dependencies (e.g., referenced source doesn't exist)
- Database constraint violations
## Testing
The export/import mechanism is fully tested with TDD. See `gsmaster/export_import_test.go` for comprehensive test coverage including:
- Export creates valid JSON files
- Import creates new records
- Import updates existing records
- Relationships are correctly restored using natural keys
- Full export/import cycle works correctly
## Notes
- **No Handlers/Routes**: This functionality is intentionally not exposed as API endpoints. Use it programmatically or via CLI tools.
- **ID Independence**: Exported data does not contain database IDs, making it portable across different database instances.
- **Idempotent**: Import can be run multiple times safely - it will update existing records rather than creating duplicates.
- **Transaction Safety**: Each import operation should ideally be wrapped in a database transaction for atomicity.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,687 @@
package gsmaster
import (
"bamort/database"
"bamort/models"
"fmt"
"path/filepath"
"gorm.io/gorm"
)
// ExportSkillImprovementCosts exports all skill improvement costs to a JSON file
func ExportSkillImprovementCosts(outputDir string) error {
var costs []models.SkillImprovementCost
if err := database.DB.Find(&costs).Error; err != nil {
return fmt.Errorf("failed to fetch skill improvement costs: %w", err)
}
// Build maps for skill category difficulties
var scds []models.SkillCategoryDifficulty
database.DB.Find(&scds)
scdMap := make(map[uint]models.SkillCategoryDifficulty)
for _, scd := range scds {
scdMap[scd.ID] = scd
}
// Get skills
var skills []models.Skill
database.DB.Find(&skills)
skillMap := make(map[uint]models.Skill)
for _, s := range skills {
skillMap[s.ID] = s
}
// Get categories
var categories []models.SkillCategory
database.DB.Find(&categories)
categoryMap := make(map[uint]models.SkillCategory)
for _, c := range categories {
categoryMap[c.ID] = c
}
// Get difficulties
var difficulties []models.SkillDifficulty
database.DB.Find(&difficulties)
difficultyMap := make(map[uint]models.SkillDifficulty)
for _, d := range difficulties {
difficultyMap[d.ID] = d
}
exportable := make([]ExportableSkillImprovementCost, len(costs))
for i, cost := range costs {
scd := scdMap[cost.SkillCategoryDifficultyID]
skill := skillMap[scd.SkillID]
category := categoryMap[scd.SkillCategoryID]
difficulty := difficultyMap[scd.SkillDifficultyID]
exportable[i] = ExportableSkillImprovementCost{
SkillName: skill.Name,
SkillSystem: skill.GameSystem,
CategoryName: category.Name,
CategorySystem: category.GameSystem,
DifficultyName: difficulty.Name,
DifficultySystem: difficulty.GameSystem,
CurrentLevel: cost.CurrentLevel,
TERequired: cost.TERequired,
}
}
return writeJSON(filepath.Join(outputDir, "skill_improvement_costs.json"), exportable)
}
// ImportSkillImprovementCosts imports skill improvement costs from a JSON file
func ImportSkillImprovementCosts(inputDir string) error {
var exportable []ExportableSkillImprovementCost
if err := readJSON(filepath.Join(inputDir, "skill_improvement_costs.json"), &exportable); err != nil {
return err
}
for _, exp := range exportable {
// Find skill
var skill models.Skill
if err := database.DB.Where("name = ? AND game_system = ?", exp.SkillName, exp.SkillSystem).First(&skill).Error; err != nil {
return fmt.Errorf("skill not found: %s: %w", exp.SkillName, err)
}
// Find category
var category models.SkillCategory
if err := database.DB.Where("name = ? AND game_system = ?", exp.CategoryName, exp.CategorySystem).First(&category).Error; err != nil {
return fmt.Errorf("category not found: %s: %w", exp.CategoryName, err)
}
// Find difficulty
var difficulty models.SkillDifficulty
if err := database.DB.Where("name = ? AND game_system = ?", exp.DifficultyName, exp.DifficultySystem).First(&difficulty).Error; err != nil {
return fmt.Errorf("difficulty not found: %s: %w", exp.DifficultyName, err)
}
// Find SkillCategoryDifficulty
var scd models.SkillCategoryDifficulty
if err := database.DB.Where("skill_id = ? AND skill_category_id = ? AND skill_difficulty_id = ?",
skill.ID, category.ID, difficulty.ID).First(&scd).Error; err != nil {
return fmt.Errorf("skill category difficulty not found: %w", err)
}
// Find or create SkillImprovementCost
var cost models.SkillImprovementCost
result := database.DB.Where("skill_category_difficulty_id = ? AND current_level = ?",
scd.ID, exp.CurrentLevel).First(&cost)
if result.Error == gorm.ErrRecordNotFound {
cost = models.SkillImprovementCost{
SkillCategoryDifficultyID: scd.ID,
CurrentLevel: exp.CurrentLevel,
TERequired: exp.TERequired,
}
if err := database.DB.Create(&cost).Error; err != nil {
return fmt.Errorf("failed to create skill improvement cost: %w", err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query skill improvement cost: %w", result.Error)
} else {
cost.TERequired = exp.TERequired
if err := database.DB.Save(&cost).Error; err != nil {
return fmt.Errorf("failed to update skill improvement cost: %w", err)
}
}
}
return nil
}
// ExportWeaponSkills exports all weapon skills to a JSON file
func ExportWeaponSkills(outputDir string) error {
var skills []models.WeaponSkill
if err := database.DB.Find(&skills).Error; err != nil {
return fmt.Errorf("failed to fetch weapon skills: %w", err)
}
// Get source map
var sources []models.Source
database.DB.Find(&sources)
sourceMap := make(map[uint]string)
for _, s := range sources {
sourceMap[s.ID] = s.Code
}
exportable := make([]ExportableWeaponSkill, len(skills))
for i, skill := range skills {
exportable[i] = ExportableWeaponSkill{
Name: skill.Name,
GameSystem: skill.GameSystem,
Beschreibung: skill.Beschreibung,
SourceCode: sourceMap[skill.SourceID],
PageNumber: skill.PageNumber,
Initialwert: skill.Initialwert,
BasisWert: skill.BasisWert,
Bonuseigenschaft: skill.Bonuseigenschaft,
Improvable: skill.Improvable,
InnateSkill: skill.InnateSkill,
Category: skill.Category,
Difficulty: skill.Difficulty,
}
}
return writeJSON(filepath.Join(outputDir, "weapon_skills.json"), exportable)
}
// ImportWeaponSkills imports weapon skills from a JSON file
func ImportWeaponSkills(inputDir string) error {
var exportable []ExportableWeaponSkill
if err := readJSON(filepath.Join(inputDir, "weapon_skills.json"), &exportable); err != nil {
return err
}
// Get source map
var sources []models.Source
database.DB.Find(&sources)
sourceMap := make(map[string]uint)
for _, s := range sources {
sourceMap[s.Code] = s.ID
}
for _, exp := range exportable {
var skill models.WeaponSkill
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&skill)
sourceID := sourceMap[exp.SourceCode]
if result.Error == gorm.ErrRecordNotFound {
skill = models.WeaponSkill{
Skill: models.Skill{
Name: exp.Name,
GameSystem: exp.GameSystem,
Beschreibung: exp.Beschreibung,
SourceID: sourceID,
PageNumber: exp.PageNumber,
Initialwert: exp.Initialwert,
BasisWert: exp.BasisWert,
Bonuseigenschaft: exp.Bonuseigenschaft,
Improvable: exp.Improvable,
InnateSkill: exp.InnateSkill,
Category: exp.Category,
Difficulty: exp.Difficulty,
},
}
if err := database.DB.Create(&skill).Error; err != nil {
return fmt.Errorf("failed to create weapon skill %s: %w", exp.Name, err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query weapon skill %s: %w", exp.Name, result.Error)
} else {
skill.Beschreibung = exp.Beschreibung
skill.SourceID = sourceID
skill.PageNumber = exp.PageNumber
skill.Initialwert = exp.Initialwert
skill.BasisWert = exp.BasisWert
skill.Bonuseigenschaft = exp.Bonuseigenschaft
skill.Improvable = exp.Improvable
skill.InnateSkill = exp.InnateSkill
skill.Category = exp.Category
skill.Difficulty = exp.Difficulty
if err := database.DB.Save(&skill).Error; err != nil {
return fmt.Errorf("failed to update weapon skill %s: %w", exp.Name, err)
}
}
}
return nil
}
// ExportEquipment exports all equipment to a JSON file
func ExportEquipment(outputDir string) error {
var equipment []models.Equipment
if err := database.DB.Find(&equipment).Error; err != nil {
return fmt.Errorf("failed to fetch equipment: %w", err)
}
// Get source map
var sources []models.Source
database.DB.Find(&sources)
sourceMap := make(map[uint]string)
for _, s := range sources {
sourceMap[s.ID] = s.Code
}
exportable := make([]ExportableEquipment, len(equipment))
for i, eq := range equipment {
exportable[i] = ExportableEquipment{
Name: eq.Name,
GameSystem: eq.GameSystem,
Beschreibung: eq.Beschreibung,
SourceCode: sourceMap[eq.SourceID],
PageNumber: eq.PageNumber,
Gewicht: eq.Gewicht,
Wert: eq.Wert,
PersonalItem: eq.PersonalItem,
}
}
return writeJSON(filepath.Join(outputDir, "equipment.json"), exportable)
}
// ImportEquipment imports equipment from a JSON file
func ImportEquipment(inputDir string) error {
var exportable []ExportableEquipment
if err := readJSON(filepath.Join(inputDir, "equipment.json"), &exportable); err != nil {
return err
}
// Get source map
var sources []models.Source
database.DB.Find(&sources)
sourceMap := make(map[string]uint)
for _, s := range sources {
sourceMap[s.Code] = s.ID
}
for _, exp := range exportable {
var eq models.Equipment
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&eq)
sourceID := sourceMap[exp.SourceCode]
if result.Error == gorm.ErrRecordNotFound {
eq = models.Equipment{
Name: exp.Name,
GameSystem: exp.GameSystem,
Beschreibung: exp.Beschreibung,
SourceID: sourceID,
PageNumber: exp.PageNumber,
Gewicht: exp.Gewicht,
Wert: exp.Wert,
PersonalItem: exp.PersonalItem,
}
if err := database.DB.Create(&eq).Error; err != nil {
return fmt.Errorf("failed to create equipment %s: %w", exp.Name, err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query equipment %s: %w", exp.Name, result.Error)
} else {
eq.Beschreibung = exp.Beschreibung
eq.SourceID = sourceID
eq.PageNumber = exp.PageNumber
eq.Gewicht = exp.Gewicht
eq.Wert = exp.Wert
eq.PersonalItem = exp.PersonalItem
if err := database.DB.Save(&eq).Error; err != nil {
return fmt.Errorf("failed to update equipment %s: %w", exp.Name, err)
}
}
}
return nil
}
// ExportWeapons exports all weapons to a JSON file
func ExportWeapons(outputDir string) error {
var weapons []models.Weapon
if err := database.DB.Find(&weapons).Error; err != nil {
return fmt.Errorf("failed to fetch weapons: %w", err)
}
// Get source map
var sources []models.Source
database.DB.Find(&sources)
sourceMap := make(map[uint]string)
for _, s := range sources {
sourceMap[s.ID] = s.Code
}
exportable := make([]ExportableWeapon, len(weapons))
for i, weapon := range weapons {
exportable[i] = ExportableWeapon{
Name: weapon.Name,
GameSystem: weapon.GameSystem,
Beschreibung: weapon.Beschreibung,
SourceCode: sourceMap[weapon.SourceID],
PageNumber: weapon.PageNumber,
Gewicht: weapon.Gewicht,
Wert: weapon.Wert,
PersonalItem: weapon.PersonalItem,
SkillRequired: weapon.SkillRequired,
Damage: weapon.Damage,
RangeNear: weapon.RangeNear,
RangeMiddle: weapon.RangeMiddle,
RangeFar: weapon.RangeFar,
}
}
return writeJSON(filepath.Join(outputDir, "weapons.json"), exportable)
}
// ImportWeapons imports weapons from a JSON file
func ImportWeapons(inputDir string) error {
var exportable []ExportableWeapon
if err := readJSON(filepath.Join(inputDir, "weapons.json"), &exportable); err != nil {
return err
}
// Get source map
var sources []models.Source
database.DB.Find(&sources)
sourceMap := make(map[string]uint)
for _, s := range sources {
sourceMap[s.Code] = s.ID
}
for _, exp := range exportable {
var weapon models.Weapon
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&weapon)
sourceID := sourceMap[exp.SourceCode]
if result.Error == gorm.ErrRecordNotFound {
weapon = models.Weapon{
Equipment: models.Equipment{
Name: exp.Name,
GameSystem: exp.GameSystem,
Beschreibung: exp.Beschreibung,
SourceID: sourceID,
PageNumber: exp.PageNumber,
Gewicht: exp.Gewicht,
Wert: exp.Wert,
PersonalItem: exp.PersonalItem,
},
SkillRequired: exp.SkillRequired,
Damage: exp.Damage,
RangeNear: exp.RangeNear,
RangeMiddle: exp.RangeMiddle,
RangeFar: exp.RangeFar,
}
if err := database.DB.Create(&weapon).Error; err != nil {
return fmt.Errorf("failed to create weapon %s: %w", exp.Name, err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query weapon %s: %w", exp.Name, result.Error)
} else {
weapon.Beschreibung = exp.Beschreibung
weapon.SourceID = sourceID
weapon.PageNumber = exp.PageNumber
weapon.Gewicht = exp.Gewicht
weapon.Wert = exp.Wert
weapon.PersonalItem = exp.PersonalItem
weapon.SkillRequired = exp.SkillRequired
weapon.Damage = exp.Damage
weapon.RangeNear = exp.RangeNear
weapon.RangeMiddle = exp.RangeMiddle
weapon.RangeFar = exp.RangeFar
if err := database.DB.Save(&weapon).Error; err != nil {
return fmt.Errorf("failed to update weapon %s: %w", exp.Name, err)
}
}
}
return nil
}
// ExportContainers exports all containers to a JSON file
func ExportContainers(outputDir string) error {
var containers []models.Container
if err := database.DB.Find(&containers).Error; err != nil {
return fmt.Errorf("failed to fetch containers: %w", err)
}
// Get source map
var sources []models.Source
database.DB.Find(&sources)
sourceMap := make(map[uint]string)
for _, s := range sources {
sourceMap[s.ID] = s.Code
}
exportable := make([]ExportableContainer, len(containers))
for i, container := range containers {
exportable[i] = ExportableContainer{
Name: container.Name,
GameSystem: container.GameSystem,
Beschreibung: container.Beschreibung,
SourceCode: sourceMap[container.SourceID],
PageNumber: container.PageNumber,
Gewicht: container.Gewicht,
Wert: container.Wert,
PersonalItem: container.PersonalItem,
Tragkraft: container.Tragkraft,
Volumen: container.Volumen,
}
}
return writeJSON(filepath.Join(outputDir, "containers.json"), exportable)
}
// ImportContainers imports containers from a JSON file
func ImportContainers(inputDir string) error {
var exportable []ExportableContainer
if err := readJSON(filepath.Join(inputDir, "containers.json"), &exportable); err != nil {
return err
}
// Get source map
var sources []models.Source
database.DB.Find(&sources)
sourceMap := make(map[string]uint)
for _, s := range sources {
sourceMap[s.Code] = s.ID
}
for _, exp := range exportable {
var container models.Container
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&container)
sourceID := sourceMap[exp.SourceCode]
if result.Error == gorm.ErrRecordNotFound {
container = models.Container{
Equipment: models.Equipment{
Name: exp.Name,
GameSystem: exp.GameSystem,
Beschreibung: exp.Beschreibung,
SourceID: sourceID,
PageNumber: exp.PageNumber,
Gewicht: exp.Gewicht,
Wert: exp.Wert,
PersonalItem: exp.PersonalItem,
},
Tragkraft: exp.Tragkraft,
Volumen: exp.Volumen,
}
if err := database.DB.Create(&container).Error; err != nil {
return fmt.Errorf("failed to create container %s: %w", exp.Name, err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query container %s: %w", exp.Name, result.Error)
} else {
container.Beschreibung = exp.Beschreibung
container.SourceID = sourceID
container.PageNumber = exp.PageNumber
container.Gewicht = exp.Gewicht
container.Wert = exp.Wert
container.PersonalItem = exp.PersonalItem
container.Tragkraft = exp.Tragkraft
container.Volumen = exp.Volumen
if err := database.DB.Save(&container).Error; err != nil {
return fmt.Errorf("failed to update container %s: %w", exp.Name, err)
}
}
}
return nil
}
// ExportTransportation exports all transportation to a JSON file
func ExportTransportation(outputDir string) error {
var transportation []models.Transportation
if err := database.DB.Find(&transportation).Error; err != nil {
return fmt.Errorf("failed to fetch transportation: %w", err)
}
// Get source map
var sources []models.Source
database.DB.Find(&sources)
sourceMap := make(map[uint]string)
for _, s := range sources {
sourceMap[s.ID] = s.Code
}
exportable := make([]ExportableTransportation, len(transportation))
for i, trans := range transportation {
exportable[i] = ExportableTransportation{
Name: trans.Name,
GameSystem: trans.GameSystem,
Beschreibung: trans.Beschreibung,
SourceCode: sourceMap[trans.SourceID],
PageNumber: trans.PageNumber,
Gewicht: trans.Gewicht,
Wert: trans.Wert,
PersonalItem: trans.PersonalItem,
Tragkraft: trans.Tragkraft,
Volumen: trans.Volumen,
}
}
return writeJSON(filepath.Join(outputDir, "transportation.json"), exportable)
}
// ImportTransportation imports transportation from a JSON file
func ImportTransportation(inputDir string) error {
var exportable []ExportableTransportation
if err := readJSON(filepath.Join(inputDir, "transportation.json"), &exportable); err != nil {
return err
}
// Get source map
var sources []models.Source
database.DB.Find(&sources)
sourceMap := make(map[string]uint)
for _, s := range sources {
sourceMap[s.Code] = s.ID
}
for _, exp := range exportable {
var trans models.Transportation
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&trans)
sourceID := sourceMap[exp.SourceCode]
if result.Error == gorm.ErrRecordNotFound {
trans = models.Transportation{
Container: models.Container{
Equipment: models.Equipment{
Name: exp.Name,
GameSystem: exp.GameSystem,
Beschreibung: exp.Beschreibung,
SourceID: sourceID,
PageNumber: exp.PageNumber,
Gewicht: exp.Gewicht,
Wert: exp.Wert,
PersonalItem: exp.PersonalItem,
},
Tragkraft: exp.Tragkraft,
Volumen: exp.Volumen,
},
}
if err := database.DB.Create(&trans).Error; err != nil {
return fmt.Errorf("failed to create transportation %s: %w", exp.Name, err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query transportation %s: %w", exp.Name, result.Error)
} else {
trans.Beschreibung = exp.Beschreibung
trans.SourceID = sourceID
trans.PageNumber = exp.PageNumber
trans.Gewicht = exp.Gewicht
trans.Wert = exp.Wert
trans.PersonalItem = exp.PersonalItem
trans.Tragkraft = exp.Tragkraft
trans.Volumen = exp.Volumen
if err := database.DB.Save(&trans).Error; err != nil {
return fmt.Errorf("failed to update transportation %s: %w", exp.Name, err)
}
}
}
return nil
}
// ExportBelieves exports all beliefs to a JSON file
func ExportBelieves(outputDir string) error {
var believes []models.Believe
if err := database.DB.Find(&believes).Error; err != nil {
return fmt.Errorf("failed to fetch believes: %w", err)
}
// Get source map
var sources []models.Source
database.DB.Find(&sources)
sourceMap := make(map[uint]string)
for _, s := range sources {
sourceMap[s.ID] = s.Code
}
exportable := make([]ExportableBelieve, len(believes))
for i, believe := range believes {
exportable[i] = ExportableBelieve{
Name: believe.Name,
GameSystem: believe.GameSystem,
Beschreibung: believe.Beschreibung,
SourceCode: sourceMap[believe.SourceID],
PageNumber: believe.PageNumber,
}
}
return writeJSON(filepath.Join(outputDir, "believes.json"), exportable)
}
// ImportBelieves imports believes from a JSON file
func ImportBelieves(inputDir string) error {
var exportable []ExportableBelieve
if err := readJSON(filepath.Join(inputDir, "believes.json"), &exportable); err != nil {
return err
}
// Get source map
var sources []models.Source
database.DB.Find(&sources)
sourceMap := make(map[string]uint)
for _, s := range sources {
sourceMap[s.Code] = s.ID
}
for _, exp := range exportable {
var believe models.Believe
result := database.DB.Where("name = ? AND game_system = ?", exp.Name, exp.GameSystem).First(&believe)
sourceID := sourceMap[exp.SourceCode]
if result.Error == gorm.ErrRecordNotFound {
believe = models.Believe{
Name: exp.Name,
GameSystem: exp.GameSystem,
Beschreibung: exp.Beschreibung,
SourceID: sourceID,
PageNumber: exp.PageNumber,
}
if err := database.DB.Create(&believe).Error; err != nil {
return fmt.Errorf("failed to create believe %s: %w", exp.Name, err)
}
} else if result.Error != nil {
return fmt.Errorf("failed to query believe %s: %w", exp.Name, result.Error)
} else {
believe.Beschreibung = exp.Beschreibung
believe.SourceID = sourceID
believe.PageNumber = exp.PageNumber
if err := database.DB.Save(&believe).Error; err != nil {
return fmt.Errorf("failed to update believe %s: %w", exp.Name, err)
}
}
}
return nil
}
+914
View File
@@ -0,0 +1,914 @@
package gsmaster
import (
"bamort/database"
"bamort/models"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"gorm.io/gorm"
)
func TestExportSkills(t *testing.T) {
setupTestEnvironment(t)
database.SetupTestDB()
// Create test data
source := getOrCreateSource("KOD", "Kodex")
skill := models.Skill{
Name: "Schwimmen",
GameSystem: "midgard",
Beschreibung: "Schwimmen im Wasser",
Initialwert: 12,
BasisWert: 0,
Bonuseigenschaft: "Gw",
Improvable: true,
InnateSkill: false,
SourceID: source.ID,
PageNumber: 42,
}
database.DB.Create(&skill)
// Export skills
tmpDir := t.TempDir()
err := ExportSkills(tmpDir)
if err != nil {
t.Fatalf("ExportSkills failed: %v", err)
}
// Verify file exists
exportFile := filepath.Join(tmpDir, "skills.json")
if _, err := os.Stat(exportFile); os.IsNotExist(err) {
t.Fatalf("Export file not created: %s", exportFile)
}
}
func TestImportSkills(t *testing.T) {
setupTestEnvironment(t)
database.SetupTestDB()
// Create source that will be referenced
source := getOrCreateSource("KOD", "Kodex")
// Export first
tmpDir := t.TempDir()
skill := models.Skill{
Name: "Klettern",
GameSystem: "midgard",
Beschreibung: "Klettern an Wänden",
Initialwert: 10,
BasisWert: 0,
Bonuseigenschaft: "Gw",
Improvable: true,
InnateSkill: false,
SourceID: source.ID,
PageNumber: 50,
}
database.DB.Create(&skill)
err := ExportSkills(tmpDir)
if err != nil {
t.Fatalf("ExportSkills failed: %v", err)
}
// Delete the skill
database.DB.Delete(&skill)
// Import back
err = ImportSkills(tmpDir)
if err != nil {
t.Fatalf("ImportSkills failed: %v", err)
}
// Verify skill was imported
var importedSkill models.Skill
err = database.DB.Where("name = ? AND game_system = ?", "Klettern", "midgard").First(&importedSkill).Error
if err != nil {
t.Fatalf("Imported skill not found: %v", err)
}
if importedSkill.Beschreibung != "Klettern an Wänden" {
t.Errorf("Expected beschreibung 'Klettern an Wänden', got '%s'", importedSkill.Beschreibung)
}
if importedSkill.Initialwert != 10 {
t.Errorf("Expected initialwert 10, got %d", importedSkill.Initialwert)
}
}
func TestImportSkillsUpdate(t *testing.T) {
setupTestEnvironment(t)
database.SetupTestDB()
source := getOrCreateSource("KOD", "Kodex")
// Create existing skill
skill := models.Skill{
Name: "Reiten",
GameSystem: "midgard",
Beschreibung: "Alte Beschreibung",
Initialwert: 8,
SourceID: source.ID,
PageNumber: 30,
}
database.DB.Create(&skill)
// Export, modify, and re-import
tmpDir := t.TempDir()
err := ExportSkills(tmpDir)
if err != nil {
t.Fatalf("ExportSkills failed: %v", err)
}
// Update skill manually
skill.Beschreibung = "Neue Beschreibung"
skill.Initialwert = 12
database.DB.Save(&skill)
// Export again with updated values
err = ExportSkills(tmpDir)
if err != nil {
t.Fatalf("ExportSkills failed: %v", err)
}
// Reset to old values
skill.Beschreibung = "Alte Beschreibung"
skill.Initialwert = 8
database.DB.Save(&skill)
// Import should update to exported values
err = ImportSkills(tmpDir)
if err != nil {
t.Fatalf("ImportSkills failed: %v", err)
}
// Verify update
var updatedSkill models.Skill
err = database.DB.Where("name = ? AND game_system = ?", "Reiten", "midgard").First(&updatedSkill).Error
if err != nil {
t.Fatalf("Updated skill not found: %v", err)
}
if updatedSkill.Beschreibung != "Neue Beschreibung" {
t.Errorf("Expected updated beschreibung 'Neue Beschreibung', got '%s'", updatedSkill.Beschreibung)
}
if updatedSkill.Initialwert != 12 {
t.Errorf("Expected updated initialwert 12, got %d", updatedSkill.Initialwert)
}
}
func TestExportImportSources(t *testing.T) {
setupTestEnvironment(t)
database.SetupTestDB()
// Create test source
source := models.Source{
Code: "ARK",
Name: "Arkanum",
GameSystem: "midgard",
IsActive: true,
}
database.DB.Create(&source)
// Export
tmpDir := t.TempDir()
err := ExportSources(tmpDir)
if err != nil {
t.Fatalf("ExportSources failed: %v", err)
}
// Delete
database.DB.Delete(&source)
// Import
err = ImportSources(tmpDir)
if err != nil {
t.Fatalf("ImportSources failed: %v", err)
}
// Verify
var imported models.Source
err = database.DB.Where("code = ?", "ARK").First(&imported).Error
if err != nil {
t.Fatalf("Imported source not found: %v", err)
}
if imported.Name != "Arkanum" {
t.Errorf("Expected name 'Arkanum', got '%s'", imported.Name)
}
}
func TestExportImportSkillCategoryDifficulty(t *testing.T) {
setupTestEnvironment(t)
database.SetupTestDB()
// Create dependencies
source := getOrCreateSource("KOD", "Kodex")
skill := models.Skill{
Name: "Tanzen",
GameSystem: "midgard",
SourceID: source.ID,
}
database.DB.Create(&skill)
category := getOrCreateCategory("Alltag", source.ID)
difficulty := getOrCreateDifficulty("leicht")
// Create relationship
scd := models.SkillCategoryDifficulty{
SkillID: skill.ID,
SkillCategoryID: category.ID,
SkillDifficultyID: difficulty.ID,
LearnCost: 5,
SCategory: category.Name,
SDifficulty: difficulty.Name,
}
database.DB.Create(&scd)
// Export
tmpDir := t.TempDir()
err := ExportSkillCategoryDifficulties(tmpDir)
if err != nil {
t.Fatalf("ExportSkillCategoryDifficulties failed: %v", err)
}
// Delete relationship
database.DB.Delete(&scd)
// Import
err = ImportSkillCategoryDifficulties(tmpDir)
if err != nil {
t.Fatalf("ImportSkillCategoryDifficulties failed: %v", err)
}
// Verify relationship was recreated
var imported models.SkillCategoryDifficulty
err = database.DB.Where("skill_id = ? AND skill_category_id = ?", skill.ID, category.ID).First(&imported).Error
if err != nil {
t.Fatalf("Imported relationship not found: %v", err)
}
if imported.LearnCost != 5 {
t.Errorf("Expected learn_cost 5, got %d", imported.LearnCost)
}
}
func TestExportImportSkillCategories(t *testing.T) {
setupTestEnvironment(t)
database.SetupTestDB()
// Create test data
source := getOrCreateSource("TEST_SC", "Test Source")
category := models.SkillCategory{
Name: "TestCategory",
GameSystem: "midgard",
SourceID: source.ID,
}
database.DB.Create(&category)
// Export
tempDir := t.TempDir()
err := ExportSkillCategories(tempDir)
if err != nil {
t.Fatalf("ExportSkillCategories failed: %v", err)
}
// Verify file was created
filename := filepath.Join(tempDir, "skill_categories.json")
if _, err := os.Stat(filename); os.IsNotExist(err) {
t.Fatalf("Export file not created: %s", filename)
}
// Delete the category
database.DB.Unscoped().Delete(&category)
// Import
err = ImportSkillCategories(tempDir)
if err != nil {
t.Fatalf("ImportSkillCategories failed: %v", err)
}
// Verify the category was recreated
var imported models.SkillCategory
result := database.DB.Where("name = ? AND game_system = ?", "TestCategory", "midgard").First(&imported)
if result.Error != nil {
t.Fatalf("Category not found after import: %v", result.Error)
}
if imported.SourceID != source.ID {
t.Errorf("Expected SourceID %d, got %d", source.ID, imported.SourceID)
}
}
func TestExportImportSkillDifficulties(t *testing.T) {
setupTestEnvironment(t)
database.SetupTestDB()
// Create test data
difficulty := models.SkillDifficulty{
Name: "TestDifficulty",
GameSystem: "midgard",
}
database.DB.Create(&difficulty)
// Export
tempDir := t.TempDir()
err := ExportSkillDifficulties(tempDir)
if err != nil {
t.Fatalf("ExportSkillDifficulties failed: %v", err)
}
// Verify file was created
filename := filepath.Join(tempDir, "skill_difficulties.json")
if _, err := os.Stat(filename); os.IsNotExist(err) {
t.Fatalf("Export file not created: %s", filename)
}
// Delete the difficulty
database.DB.Unscoped().Delete(&difficulty)
// Import
err = ImportSkillDifficulties(tempDir)
if err != nil {
t.Fatalf("ImportSkillDifficulties failed: %v", err)
}
// Verify the difficulty was recreated
var imported models.SkillDifficulty
result := database.DB.Where("name = ? AND game_system = ?", "TestDifficulty", "midgard").First(&imported)
if result.Error != nil {
t.Fatalf("Difficulty not found after import: %v", result.Error)
}
}
func TestExportImportSpells(t *testing.T) {
setupTestEnvironment(t)
database.SetupTestDB()
// Create test data
source := getOrCreateSource("TEST_SP", "Test Spell Source")
spell := models.Spell{
Name: "TestSpell",
GameSystem: "midgard",
Beschreibung: "Test description",
SourceID: source.ID,
PageNumber: 42,
Bonus: 5,
Stufe: 3,
AP: "2",
Art: "Gestenzauber",
Zauberdauer: "10 sec",
Reichweite: "10m",
Wirkungsziel: "Person",
Wirkungsbereich: "1 Person",
Wirkungsdauer: "1h",
Ursprung: "Elben",
Category: "normal",
LearningCategory: "default",
}
database.DB.Create(&spell)
// Export
tempDir := t.TempDir()
err := ExportSpells(tempDir)
if err != nil {
t.Fatalf("ExportSpells failed: %v", err)
}
// Verify file was created
filename := filepath.Join(tempDir, "spells.json")
if _, err := os.Stat(filename); os.IsNotExist(err) {
t.Fatalf("Export file not created: %s", filename)
}
// Modify the spell
spell.Beschreibung = "Old description"
spell.Bonus = 3
database.DB.Save(&spell)
// Import (should update)
err = ImportSpells(tempDir)
if err != nil {
t.Fatalf("ImportSpells failed: %v", err)
}
// Verify the spell was updated
var imported models.Spell
result := database.DB.Where("name = ? AND game_system = ?", "TestSpell", "midgard").First(&imported)
if result.Error != nil {
t.Fatalf("Spell not found after import: %v", result.Error)
}
if imported.Beschreibung != "Test description" {
t.Errorf("Expected description 'Test description', got '%s'", imported.Beschreibung)
}
if imported.Bonus != 5 {
t.Errorf("Expected bonus 5, got %d", imported.Bonus)
}
if imported.Stufe != 3 {
t.Errorf("Expected level 3, got %d", imported.Stufe)
}
}
func TestExportImportAll(t *testing.T) {
setupTestEnvironment(t)
database.SetupTestDB()
// Create test data
source := getOrCreateSource("TEST_ALL", "Test All Source")
category := models.SkillCategory{Name: "AllCategory", GameSystem: "midgard", SourceID: source.ID}
database.DB.Create(&category)
difficulty := models.SkillDifficulty{Name: "AllDifficulty", GameSystem: "midgard"}
database.DB.Create(&difficulty)
skill := models.Skill{
Name: "AllSkill",
GameSystem: "midgard",
SourceID: source.ID,
Initialwert: 10,
}
database.DB.Create(&skill)
spell := models.Spell{
Name: "AllSpell",
GameSystem: "midgard",
SourceID: source.ID,
Stufe: 2,
}
database.DB.Create(&spell)
// Export all
tempDir := t.TempDir()
err := ExportAll(tempDir)
if err != nil {
t.Fatalf("ExportAll failed: %v", err)
}
// Verify all files were created
files := []string{
"sources.json",
"character_classes.json",
"skill_categories.json",
"skill_difficulties.json",
"spell_schools.json",
"skills.json",
"skill_category_difficulties.json",
"spells.json",
"class_category_ep_costs.json",
"class_spell_school_ep_costs.json",
"spell_level_le_costs.json",
"skill_improvement_costs.json",
"weapon_skills.json",
"equipment.json",
"weapons.json",
"containers.json",
"transportation.json",
"believes.json",
}
for _, file := range files {
filename := filepath.Join(tempDir, file)
if _, err := os.Stat(filename); os.IsNotExist(err) {
t.Errorf("Export file not created: %s", filename)
}
}
// Delete all test data
database.DB.Unscoped().Delete(&spell)
database.DB.Unscoped().Delete(&skill)
database.DB.Unscoped().Delete(&difficulty)
database.DB.Unscoped().Delete(&category)
// Don't delete source to avoid FK constraints
// Import all
err = ImportAll(tempDir)
if err != nil {
t.Fatalf("ImportAll failed: %v", err)
}
// Verify all data was recreated
var importedCategory models.SkillCategory
if err := database.DB.Where("name = ? AND game_system = ?", "AllCategory", "midgard").First(&importedCategory).Error; err != nil {
t.Errorf("Category not found after import: %v", err)
}
var importedDifficulty models.SkillDifficulty
if err := database.DB.Where("name = ? AND game_system = ?", "AllDifficulty", "midgard").First(&importedDifficulty).Error; err != nil {
t.Errorf("Difficulty not found after import: %v", err)
}
var importedSkill models.Skill
if err := database.DB.Where("name = ? AND game_system = ?", "AllSkill", "midgard").First(&importedSkill).Error; err != nil {
t.Errorf("Skill not found after import: %v", err)
}
var importedSpell models.Spell
if err := database.DB.Where("name = ? AND game_system = ?", "AllSpell", "midgard").First(&importedSpell).Error; err != nil {
t.Errorf("Spell not found after import: %v", err)
}
}
func TestExportAll_live(t *testing.T) {
setupTestEnvironment(t)
database.SetupTestDB(false)
// Export all
tempDir := t.TempDir()
err := ExportAll(tempDir)
if err != nil {
t.Fatalf("ExportAll failed: %v", err)
}
// Verify all files were created
files := []string{"sources.json", "skill_categories.json", "skill_difficulties.json", "skills.json", "spells.json", "skill_category_difficulties.json"}
for _, file := range files {
filename := filepath.Join(tempDir, file)
if _, err := os.Stat(filename); os.IsNotExist(err) {
t.Errorf("Export file not created: %s", filename)
}
}
assert.Equal(t, len(files), 6)
}
func TestExportImportWeaponSkills(t *testing.T) {
setupTestEnvironment(t)
database.SetupTestDB()
source := getOrCreateSource("TEST_WS", "Test Weapon Source")
weaponSkill := models.WeaponSkill{
Skill: models.Skill{
Name: "Langschwert",
GameSystem: "midgard",
Beschreibung: "Langschwert Waffenfertigkeiten",
SourceID: source.ID,
PageNumber: 50,
Initialwert: 10,
BasisWert: 5,
Bonuseigenschaft: "St",
Improvable: true,
InnateSkill: false,
Category: "Waffen",
Difficulty: "normal",
},
}
database.DB.Create(&weaponSkill)
tempDir := t.TempDir()
err := ExportWeaponSkills(tempDir)
if err != nil {
t.Fatalf("ExportWeaponSkills failed: %v", err)
}
filename := filepath.Join(tempDir, "weapon_skills.json")
if _, err := os.Stat(filename); os.IsNotExist(err) {
t.Fatalf("Export file not created: %s", filename)
}
database.DB.Unscoped().Delete(&weaponSkill)
err = ImportWeaponSkills(tempDir)
if err != nil {
t.Fatalf("ImportWeaponSkills failed: %v", err)
}
var imported models.WeaponSkill
result := database.DB.Where("name = ? AND game_system = ?", "Langschwert", "midgard").First(&imported)
if result.Error != nil {
t.Fatalf("Weapon skill not found after import: %v", result.Error)
}
assert.Equal(t, "Langschwert Waffenfertigkeiten", imported.Beschreibung)
assert.Equal(t, 10, imported.Initialwert)
assert.Equal(t, 5, imported.BasisWert)
}
func TestExportImportEquipment(t *testing.T) {
setupTestEnvironment(t)
database.SetupTestDB()
source := getOrCreateSource("TEST_EQ", "Test Equipment Source")
equipment := models.Equipment{
Name: "Seil",
GameSystem: "midgard",
Beschreibung: "10m langes Hanfseil",
SourceID: source.ID,
PageNumber: 75,
Gewicht: 2.5,
Wert: 15.0,
PersonalItem: false,
}
database.DB.Create(&equipment)
tempDir := t.TempDir()
err := ExportEquipment(tempDir)
if err != nil {
t.Fatalf("ExportEquipment failed: %v", err)
}
filename := filepath.Join(tempDir, "equipment.json")
if _, err := os.Stat(filename); os.IsNotExist(err) {
t.Fatalf("Export file not created: %s", filename)
}
equipment.Wert = 10.0
database.DB.Save(&equipment)
err = ImportEquipment(tempDir)
if err != nil {
t.Fatalf("ImportEquipment failed: %v", err)
}
var imported models.Equipment
result := database.DB.Where("name = ? AND game_system = ?", "Seil", "midgard").First(&imported)
if result.Error != nil {
t.Fatalf("Equipment not found after import: %v", result.Error)
}
assert.Equal(t, 15.0, imported.Wert)
assert.Equal(t, 2.5, imported.Gewicht)
}
func TestExportImportWeapons(t *testing.T) {
setupTestEnvironment(t)
database.SetupTestDB()
source := getOrCreateSource("TEST_WP", "Test Weapon Source")
weapon := models.Weapon{
Equipment: models.Equipment{
Name: "Kurzschwert",
GameSystem: "midgard",
Beschreibung: "Einhändiges Kurzschwert",
SourceID: source.ID,
PageNumber: 80,
Gewicht: 1.5,
Wert: 50.0,
PersonalItem: false,
},
SkillRequired: "Langschwert",
Damage: "1W6+1",
RangeNear: 0,
RangeMiddle: 0,
RangeFar: 0,
}
database.DB.Create(&weapon)
tempDir := t.TempDir()
err := ExportWeapons(tempDir)
if err != nil {
t.Fatalf("ExportWeapons failed: %v", err)
}
filename := filepath.Join(tempDir, "weapons.json")
if _, err := os.Stat(filename); os.IsNotExist(err) {
t.Fatalf("Export file not created: %s", filename)
}
weapon.Damage = "1W6"
database.DB.Save(&weapon)
err = ImportWeapons(tempDir)
if err != nil {
t.Fatalf("ImportWeapons failed: %v", err)
}
var imported models.Weapon
result := database.DB.Where("name = ? AND game_system = ?", "Kurzschwert", "midgard").First(&imported)
if result.Error != nil {
t.Fatalf("Weapon not found after import: %v", result.Error)
}
assert.Equal(t, "1W6+1", imported.Damage)
assert.Equal(t, "Langschwert", imported.SkillRequired)
}
func TestExportImportContainers(t *testing.T) {
setupTestEnvironment(t)
database.SetupTestDB()
source := getOrCreateSource("TEST_CT", "Test Container Source")
container := models.Container{
Equipment: models.Equipment{
Name: "Rucksack",
GameSystem: "midgard",
Beschreibung: "Großer Lederrucksack",
SourceID: source.ID,
PageNumber: 85,
Gewicht: 1.0,
Wert: 20.0,
PersonalItem: false,
},
Tragkraft: 30.0,
Volumen: 50.0,
}
database.DB.Create(&container)
tempDir := t.TempDir()
err := ExportContainers(tempDir)
if err != nil {
t.Fatalf("ExportContainers failed: %v", err)
}
filename := filepath.Join(tempDir, "containers.json")
if _, err := os.Stat(filename); os.IsNotExist(err) {
t.Fatalf("Export file not created: %s", filename)
}
container.Tragkraft = 25.0
database.DB.Save(&container)
err = ImportContainers(tempDir)
if err != nil {
t.Fatalf("ImportContainers failed: %v", err)
}
var imported models.Container
result := database.DB.Where("name = ? AND game_system = ?", "Rucksack", "midgard").First(&imported)
if result.Error != nil {
t.Fatalf("Container not found after import: %v", result.Error)
}
assert.Equal(t, 30.0, imported.Tragkraft)
assert.Equal(t, 50.0, imported.Volumen)
}
func TestExportImportTransportation(t *testing.T) {
setupTestEnvironment(t)
database.SetupTestDB()
source := getOrCreateSource("TEST_TR", "Test Transport Source")
transportation := models.Transportation{
Container: models.Container{
Equipment: models.Equipment{
Name: "Pferdewagen",
GameSystem: "midgard",
Beschreibung: "Zweirädriger Wagen",
SourceID: source.ID,
PageNumber: 90,
Gewicht: 100.0,
Wert: 200.0,
PersonalItem: false,
},
Tragkraft: 500.0,
Volumen: 1000.0,
},
}
database.DB.Create(&transportation)
tempDir := t.TempDir()
err := ExportTransportation(tempDir)
if err != nil {
t.Fatalf("ExportTransportation failed: %v", err)
}
filename := filepath.Join(tempDir, "transportation.json")
if _, err := os.Stat(filename); os.IsNotExist(err) {
t.Fatalf("Export file not created: %s", filename)
}
transportation.Tragkraft = 450.0
database.DB.Save(&transportation)
err = ImportTransportation(tempDir)
if err != nil {
t.Fatalf("ImportTransportation failed: %v", err)
}
var imported models.Transportation
result := database.DB.Where("name = ? AND game_system = ?", "Pferdewagen", "midgard").First(&imported)
if result.Error != nil {
t.Fatalf("Transportation not found after import: %v", result.Error)
}
assert.Equal(t, 500.0, imported.Tragkraft)
assert.Equal(t, 1000.0, imported.Volumen)
}
func TestExportImportBelieves(t *testing.T) {
setupTestEnvironment(t)
database.SetupTestDB()
source := getOrCreateSource("TEST_BL", "Test Believe Source")
believe := models.Believe{
Name: "Kirche des Lichts",
GameSystem: "midgard",
Beschreibung: "Hauptreligion in Valian",
SourceID: source.ID,
PageNumber: 95,
}
database.DB.Create(&believe)
tempDir := t.TempDir()
err := ExportBelieves(tempDir)
if err != nil {
t.Fatalf("ExportBelieves failed: %v", err)
}
filename := filepath.Join(tempDir, "believes.json")
if _, err := os.Stat(filename); os.IsNotExist(err) {
t.Fatalf("Export file not created: %s", filename)
}
believe.Beschreibung = "Alte Beschreibung"
database.DB.Save(&believe)
err = ImportBelieves(tempDir)
if err != nil {
t.Fatalf("ImportBelieves failed: %v", err)
}
var imported models.Believe
result := database.DB.Where("name = ? AND game_system = ?", "Kirche des Lichts", "midgard").First(&imported)
if result.Error != nil {
t.Fatalf("Believe not found after import: %v", result.Error)
}
assert.Equal(t, "Hauptreligion in Valian", imported.Beschreibung)
assert.Equal(t, 95, imported.PageNumber)
}
func TestExportImportSkillImprovementCosts(t *testing.T) {
setupTestEnvironment(t)
database.SetupTestDB()
// Create dependencies that already exist in test DB
// Use existing skill, category, difficulty from test database
var skill models.Skill
if err := database.DB.Where("name = ?", "Abrichten").First(&skill).Error; err != nil {
t.Skip("Test skill not found in database, skipping test")
}
var category models.SkillCategory
if err := database.DB.First(&category).Error; err != nil {
t.Skip("No skill category found in database, skipping test")
}
var difficulty models.SkillDifficulty
if err := database.DB.First(&difficulty).Error; err != nil {
t.Skip("No skill difficulty found in database, skipping test")
}
// Find or create SkillCategoryDifficulty
var scd models.SkillCategoryDifficulty
err := database.DB.Where("skill_id = ? AND skill_category_id = ? AND skill_difficulty_id = ?",
skill.ID, category.ID, difficulty.ID).First(&scd).Error
if err == gorm.ErrRecordNotFound {
scd = models.SkillCategoryDifficulty{
SkillID: skill.ID,
SkillCategoryID: category.ID,
SkillDifficultyID: difficulty.ID,
LearnCost: 10,
SCategory: category.Name,
SDifficulty: difficulty.Name,
}
database.DB.Create(&scd)
} else if err != nil {
t.Fatalf("Failed to query SkillCategoryDifficulty: %v", err)
}
// Create SkillImprovementCost
improvementCost := models.SkillImprovementCost{
SkillCategoryDifficultyID: scd.ID,
CurrentLevel: 15, // Use unique level to avoid conflicts
TERequired: 5,
}
database.DB.Create(&improvementCost)
// Export
tempDir := t.TempDir()
err = ExportSkillImprovementCosts(tempDir)
if err != nil {
t.Fatalf("ExportSkillImprovementCosts failed: %v", err)
}
filename := filepath.Join(tempDir, "skill_improvement_costs.json")
if _, err := os.Stat(filename); os.IsNotExist(err) {
t.Fatalf("Export file not created: %s", filename)
}
// Modify the record
improvementCost.TERequired = 7
database.DB.Save(&improvementCost)
// Import should restore original value
err = ImportSkillImprovementCosts(tempDir)
if err != nil {
t.Fatalf("ImportSkillImprovementCosts failed: %v", err)
}
var imported models.SkillImprovementCost
result := database.DB.Where("skill_category_difficulty_id = ? AND current_level = ?", scd.ID, 15).First(&imported)
if result.Error != nil {
t.Fatalf("SkillImprovementCost not found after import: %v", result.Error)
}
// Should be restored to original value from export
assert.Equal(t, 5, imported.TERequired)
assert.Equal(t, 15, imported.CurrentLevel)
}
-45
View File
@@ -128,51 +128,6 @@ type Believe struct {
PageNumber int `json:"page_number,omitempty"` // Seitenzahl im Quellenbuch
}
/*
func (object *LookupList) Create() error {
gameSystem := "midgard"
object.GameSystem = gameSystem
err := database.DB.Transaction(func(tx *gorm.DB) error {
// Save the main character record
if err := tx.Create(&object).Error; err != nil {
return fmt.Errorf("failed to save Lookup: %w", err)
}
return nil
})
return err
}
func (object *LookupList) First(value string) error {
gameSystem := "midgard"
err := database.DB.First(&object, "game_system=? AND name!='Placeholder' AND name = ?", gameSystem, value).Error
if err != nil {
// zauber found
return err
}
return nil
}
func (object *LookupList) FirstId(value uint) error {
gameSystem := "midgard"
err := database.DB.First(&object, "game_system=? AND name!='Placeholder' AND id = ?", gameSystem, value).Error
if err != nil {
// zauber found
return err
}
return nil
}
func (object *LookupList) Save() error {
err := database.DB.Save(&object).Error
if err != nil {
// zauber found
return err
}
return nil
}
*/
func (object *Skill) TableName() string {
dbPrefix := "gsm"
return dbPrefix + "_" + "skills"