package drawio_test import ( "io" "io/fs" "os" "os/exec" "path" "strings" "testing" "time" "git.mousesoft.ru/ms/drawio-export/pkg/drawio" "github.com/stretchr/testify/assert" ) // Команда оболочки ОС type command struct { cmd string // Команда args []string // Аргументы команды } // Файл с диаграммами type source struct { output string // Папка вывода для ExportFile data string // Данные файла с диаграммами diagrams []string // Срез имён диаграмм commands []command // Ожидаемые команды экспорта диаграмм } // Элемент папки с файлами type dirEntry struct { name string // Имя элемента isDir bool // Истина, если элемент является папкой } var ( _ os.DirEntry = (*dirEntry)(nil) _ os.FileInfo = (*dirEntry)(nil) ) func (entry dirEntry) Name() string { return entry.name } func (entry dirEntry) IsDir() bool { return entry.isDir } func (entry dirEntry) Type() fs.FileMode { if entry.isDir { return fs.ModeDir } return 0 } func (entry dirEntry) Info() (fs.FileInfo, error) { return nil, nil } func (entry dirEntry) Size() int64 { return 100 } func (entry dirEntry) Mode() fs.FileMode { return entry.Type() } func (entry dirEntry) ModTime() time.Time { return time.Now() } func (entry dirEntry) Sys() any { return nil } // Конвертация команд в информацию о командах func cmd2info(commands []*exec.Cmd) []command { cmdInfo := make([]command, len(commands)) for i, cmd := range commands { cmdInfo[i] = command{ cmd: cmd.Path, args: make([]string, len(cmd.Args)), } copy(cmdInfo[i].args, cmd.Args) } return cmdInfo } type exportTest struct { name string // Наименование теста dirs map[string][]os.DirEntry // Папки файлов с диаграммами files map[string]source // Файлы с диаграммами } // Информация об ожидаемых командах при экспорте папки func (test exportTest) dirCommands(dir string, recursive bool) []command { commands := make([]command, 0, 4) if tc, ok := test.dirs[dir]; ok { for _, entry := range tc { path := path.Join(dir, entry.Name()) if entry.IsDir() && recursive { commands = append(commands, test.dirCommands(path, true)...) } else { commands = append(commands, test.files[path].commands...) } } } return commands } // Тестовые данные var testData = []exportTest{ { name: "positive case", dirs: map[string][]os.DirEntry{ "source": { dirEntry{name: "diagrams.drawio", isDir: false}, }, }, files: map[string]source{ "source/diagrams.drawio": { data: ` `, diagrams: []string{"1", "2", "3"}, commands: []command{ { cmd: "drawio", args: []string{ "drawio", "--page-index", "0", "--output", "export/diagrams-1.pdf", "--export", "source/diagrams.drawio", }, }, { cmd: "drawio", args: []string{ "drawio", "--page-index", "1", "--output", "export/diagrams-2.pdf", "--export", "source/diagrams.drawio", }, }, { cmd: "drawio", args: []string{ "drawio", "--page-index", "2", "--output", "export/diagrams-3.pdf", "--export", "source/diagrams.drawio", }, }, }, }, }, }, { name: "invalid source", dirs: map[string][]os.DirEntry{ "source": { dirEntry{name: "diagrams.drawio", isDir: false}, }, }, files: map[string]source{ "source/diagrams.drawio": { data: ` `, diagrams: []string{}, }, }, }, { name: "nested dirs", dirs: map[string][]os.DirEntry{ "source": { dirEntry{name: "diagrams.drawio", isDir: false}, dirEntry{name: "subdir", isDir: true}, dirEntry{name: "additional.xml", isDir: false}, }, "source/subdir": { dirEntry{name: "Вложенные диаграммы.drawio", isDir: false}, }, }, files: map[string]source{ "source/diagrams.drawio": { data: ` `, diagrams: []string{"1", "2", "3"}, commands: []command{ { cmd: "drawio", args: []string{ "drawio", "--page-index", "0", "--output", "export/diagrams-1.pdf", "--export", "source/diagrams.drawio", }, }, { cmd: "drawio", args: []string{ "drawio", "--page-index", "1", "--output", "export/diagrams-2.pdf", "--export", "source/diagrams.drawio", }, }, { cmd: "drawio", args: []string{ "drawio", "--page-index", "2", "--output", "export/diagrams-3.pdf", "--export", "source/diagrams.drawio", }, }, }, }, "source/additional.xml": { data: ` `, diagrams: []string{"Один", "Два"}, commands: []command{ { cmd: "drawio", args: []string{ "drawio", "--page-index", "0", "--output", "export/additional-Один.pdf", "--export", "source/additional.xml", }, }, { cmd: "drawio", args: []string{ "drawio", "--page-index", "1", "--output", "export/additional-Два.pdf", "--export", "source/additional.xml", }, }, }, }, "source/subdir/Вложенные диаграммы.drawio": { output: "export/subdir", data: ` `, diagrams: []string{"Первая диаграмма", "Вторая диаграмма"}, commands: []command{ { cmd: "drawio", args: []string{ "drawio", "--page-index", "0", "--output", "export/subdir/Вложенные диаграммы-Первая диаграмма.pdf", "--export", "source/subdir/Вложенные диаграммы.drawio", }, }, { cmd: "drawio", args: []string{ "drawio", "--page-index", "1", "--output", "export/subdir/Вложенные диаграммы-Вторая диаграмма.pdf", "--export", "source/subdir/Вложенные диаграммы.drawio", }, }, }, }, }, }, } // Инициализация тестовых данных func init() { for _, test := range testData { for _, src := range test.files { for i := range src.commands { cmd := exec.Command("drawio") src.commands[i].cmd = cmd.Path } } } } func TestDiagrams(t *testing.T) { for _, test := range testData { t.Run(test.name, func(t *testing.T) { for filePath, source := range test.files { t.Run(filePath, func(t *testing.T) { diagrams, err := drawio.Diagrams(strings.NewReader(source.data)) assert.NoError(t, err) assert.ElementsMatch(t, source.diagrams, diagrams) }) } }) } } func TestExportFile(t *testing.T) { for _, test := range testData { t.Run(test.name, func(t *testing.T) { for filePath, source := range test.files { var openOpt drawio.Option = drawio.WithNop() if len(source.output) > 0 { openOpt = drawio.WithOutput(source.output) } t.Run(filePath, func(t *testing.T) { var ( openFileCalls = []string{} exp = drawio.New( drawio.WithOpenFile(func(s string) (io.ReadCloser, error) { openFileCalls = append(openFileCalls, s) return io.NopCloser(strings.NewReader(source.data)), nil }), openOpt, ) ) commands, err := exp.ExportFile(filePath, []string{}) assert.NoError(t, err) if assert.Equal(t, 1, len(openFileCalls)) { assert.Equal(t, filePath, openFileCalls[0]) } assert.ElementsMatch(t, source.commands, cmd2info(commands)) }) } }) } } func TestExportDir(t *testing.T) { for _, recursive := range []bool{false, true} { var ( name string recursiveOption drawio.Option ) if recursive { name = "recursive" recursiveOption = drawio.WithRecursive() } else { name = "non-recursive" recursiveOption = drawio.WithNop() } t.Run(name, func(t *testing.T) { for _, test := range testData { t.Run(test.name, func(t *testing.T) { var ( openFileCalls = []string{} exp = drawio.New( drawio.WithOpenFile(func(s string) (io.ReadCloser, error) { openFileCalls = append(openFileCalls, s) return io.NopCloser( strings.NewReader(test.files[s].data), ), nil }), drawio.WithReadDir(func(s string) ([]os.DirEntry, error) { return test.dirs[s], nil }), recursiveOption, ) ) for dir := range test.dirs { pathDir, _ := path.Split(dir) if len(pathDir) > 0 { continue } t.Run(dir, func(t *testing.T) { openFileCalls = openFileCalls[:0] commands, err := exp.ExportDir(dir, []string{}) assert.NoError(t, err) assert.ElementsMatch(t, test.dirCommands(dir, recursive), cmd2info(commands)) }) } }) } }) } }