210 lines
6.5 KiB
Go
210 lines
6.5 KiB
Go
package main
|
|
|
|
import (
|
|
"archive/zip"
|
|
"context"
|
|
"database/sql"
|
|
"encoding/csv"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
func TestParseDocumentXMLExtractsTableCells(t *testing.T) {
|
|
xml := `<?xml version="1.0" encoding="UTF-8"?>
|
|
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
<w:body>
|
|
<w:tbl>
|
|
<w:tr>
|
|
<w:tc><w:tcPr><w:gridSpan w:val="2"/></w:tcPr><w:p><w:r><w:t>报备表</w:t></w:r></w:p></w:tc>
|
|
</w:tr>
|
|
<w:tr>
|
|
<w:tc><w:p><w:r><w:t>序号</w:t></w:r></w:p></w:tc>
|
|
<w:tc><w:p><w:r><w:t>事件过程</w:t></w:r></w:p></w:tc>
|
|
</w:tr>
|
|
<w:tr>
|
|
<w:tc><w:p><w:r><w:t>1</w:t></w:r></w:p></w:tc>
|
|
<w:tc><w:p><w:r><w:t>起因</w:t><w:br/><w:t>处理经过</w:t></w:r></w:p></w:tc>
|
|
</w:tr>
|
|
</w:tbl>
|
|
</w:body>
|
|
</w:document>`
|
|
|
|
tables, err := parseDocumentXML([]byte(xml))
|
|
if err != nil {
|
|
t.Fatalf("parseDocumentXML returned error: %v", err)
|
|
}
|
|
if len(tables) != 1 {
|
|
t.Fatalf("got %d tables, want 1", len(tables))
|
|
}
|
|
if got := tables[0].Rows[0].Cells[0].Text; got != "报备表" {
|
|
t.Fatalf("title cell text = %q, want 报备表", got)
|
|
}
|
|
if got := tables[0].Rows[0].Cells[0].ColSpan; got != 2 {
|
|
t.Fatalf("title cell colspan = %d, want 2", got)
|
|
}
|
|
if got := tables[0].Rows[2].Cells[1].Text; got != "起因\n处理经过" {
|
|
t.Fatalf("multiline text = %q, want 起因\\n处理经过", got)
|
|
}
|
|
}
|
|
|
|
func TestInferHeaderFieldsSkipsTitleRow(t *testing.T) {
|
|
table := Table{
|
|
Rows: []Row{
|
|
{Cells: []Cell{{Text: "报备表", ColSpan: 2}}},
|
|
{Cells: []Cell{{Text: "序号", ColSpan: 1}, {Text: "事件过程", ColSpan: 1}}},
|
|
{Cells: []Cell{{Text: "1", ColSpan: 1}, {Text: "客户反映设备位置不合理", ColSpan: 1}}},
|
|
},
|
|
}
|
|
|
|
fields := inferFields(table)
|
|
if len(fields) != 2 {
|
|
t.Fatalf("got %d fields, want 2", len(fields))
|
|
}
|
|
if fields[0].Key != "序号" || fields[0].Value != "1" || fields[0].SourceKind != "header_row" {
|
|
t.Fatalf("unexpected first field: %#v", fields[0])
|
|
}
|
|
if fields[1].Key != "事件过程" || fields[1].Value != "客户反映设备位置不合理" {
|
|
t.Fatalf("unexpected second field: %#v", fields[1])
|
|
}
|
|
}
|
|
|
|
func TestInferHeaderFieldsAlignsByLogicalColumnWithColSpan(t *testing.T) {
|
|
table := Table{
|
|
Rows: []Row{
|
|
{Cells: []Cell{{Text: "序号", ColIndex: 0, ColSpan: 1}, {Text: "客户信息", ColIndex: 1, ColSpan: 2}, {Text: "工单编号", ColIndex: 3, ColSpan: 1}}},
|
|
{Cells: []Cell{{Text: "1", ColIndex: 0, ColSpan: 1}, {Text: "高先生", ColIndex: 1, ColSpan: 1}, {Text: "靖远县乌兰镇", ColIndex: 2, ColSpan: 1}, {Text: "9726060110063361", ColIndex: 3, ColSpan: 1}}},
|
|
},
|
|
}
|
|
|
|
fields := inferFields(table)
|
|
got := fieldMap(fields)
|
|
if got["序号"] != "1" {
|
|
t.Fatalf("序号 = %q, want 1", got["序号"])
|
|
}
|
|
if got["客户信息"] != "高先生\n靖远县乌兰镇" {
|
|
t.Fatalf("客户信息 = %q, want combined logical-column value", got["客户信息"])
|
|
}
|
|
if got["工单编号"] != "9726060110063361" {
|
|
t.Fatalf("工单编号 = %q, want 9726060110063361", got["工单编号"])
|
|
}
|
|
}
|
|
|
|
func TestImportDocumentWritesSQLiteTables(t *testing.T) {
|
|
dbPath := filepath.Join(t.TempDir(), "import.sqlite")
|
|
db, err := sql.Open("sqlite", dbPath)
|
|
if err != nil {
|
|
t.Fatalf("open sqlite: %v", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
xml := `<?xml version="1.0" encoding="UTF-8"?>
|
|
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
<w:body>
|
|
<w:tbl>
|
|
<w:tr><w:tc><w:tcPr><w:gridSpan w:val="2"/></w:tcPr><w:p><w:r><w:t>报备表</w:t></w:r></w:p></w:tc></w:tr>
|
|
<w:tr><w:tc><w:p><w:r><w:t>序号</w:t></w:r></w:p></w:tc><w:tc><w:p><w:r><w:t>事件过程</w:t></w:r></w:p></w:tc></w:tr>
|
|
<w:tr><w:tc><w:p><w:r><w:t>1</w:t></w:r></w:p></w:tc><w:tc><w:p><w:r><w:t>客户反映设备位置不合理</w:t></w:r></w:p></w:tc></w:tr>
|
|
</w:tbl>
|
|
</w:body>
|
|
</w:document>`
|
|
|
|
docxPath := filepath.Join(t.TempDir(), "sample.docx")
|
|
if err := writeTestDocx(docxPath, []byte(xml)); err != nil {
|
|
t.Fatalf("write test docx: %v", err)
|
|
}
|
|
|
|
if err := importDocument(context.Background(), db, docxPath); err != nil {
|
|
t.Fatalf("importDocument: %v", err)
|
|
}
|
|
|
|
var cells int
|
|
if err := db.QueryRow(`SELECT COUNT(*) FROM cells`).Scan(&cells); err != nil {
|
|
t.Fatalf("count cells: %v", err)
|
|
}
|
|
if cells != 5 {
|
|
t.Fatalf("got %d cells, want 5", cells)
|
|
}
|
|
|
|
var value string
|
|
if err := db.QueryRow(`SELECT value FROM fields WHERE key = '事件过程'`).Scan(&value); err != nil {
|
|
t.Fatalf("query field: %v", err)
|
|
}
|
|
if value != "客户反映设备位置不合理" {
|
|
t.Fatalf("field value = %q", value)
|
|
}
|
|
}
|
|
|
|
func TestRunExportsFieldsCSV(t *testing.T) {
|
|
dir := t.TempDir()
|
|
dbPath := filepath.Join(dir, "import.sqlite")
|
|
outPath := filepath.Join(dir, "fields.csv")
|
|
xml := `<?xml version="1.0" encoding="UTF-8"?>
|
|
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
<w:body>
|
|
<w:tbl>
|
|
<w:tr><w:tc><w:p><w:r><w:t>字段</w:t></w:r></w:p></w:tc><w:tc><w:p><w:r><w:t>内容</w:t></w:r></w:p></w:tc></w:tr>
|
|
<w:tr><w:tc><w:p><w:r><w:t>工单编号</w:t></w:r></w:p></w:tc><w:tc><w:p><w:r><w:t>9726060110063361</w:t></w:r></w:p></w:tc></w:tr>
|
|
</w:tbl>
|
|
</w:body>
|
|
</w:document>`
|
|
docxPath := filepath.Join(dir, "sample.docx")
|
|
if err := writeTestDocx(docxPath, []byte(xml)); err != nil {
|
|
t.Fatalf("write test docx: %v", err)
|
|
}
|
|
|
|
if err := run([]string{"-reset", "-quiet", "-db", dbPath, docxPath}); err != nil {
|
|
t.Fatalf("import run: %v", err)
|
|
}
|
|
if err := run([]string{"export-fields", "-db", dbPath, "-out", outPath}); err != nil {
|
|
t.Fatalf("export run: %v", err)
|
|
}
|
|
|
|
f, err := os.Open(outPath)
|
|
if err != nil {
|
|
t.Fatalf("open csv: %v", err)
|
|
}
|
|
defer f.Close()
|
|
records, err := csv.NewReader(f).ReadAll()
|
|
if err != nil {
|
|
t.Fatalf("read csv: %v", err)
|
|
}
|
|
if len(records) != 3 {
|
|
t.Fatalf("got %d csv records, want header + 2 fields: %#v", len(records), records)
|
|
}
|
|
if records[0][0] != "filename" || records[0][1] != "key" || records[0][2] != "value" {
|
|
t.Fatalf("unexpected header: %#v", records[0])
|
|
}
|
|
if records[2][1] != "内容" || records[2][2] != "9726060110063361" {
|
|
t.Fatalf("unexpected exported field: %#v", records[2])
|
|
}
|
|
}
|
|
|
|
func fieldMap(fields []Field) map[string]string {
|
|
out := map[string]string{}
|
|
for _, field := range fields {
|
|
out[field.Key] = field.Value
|
|
}
|
|
return out
|
|
}
|
|
|
|
func writeTestDocx(path string, documentXML []byte) error {
|
|
f, err := os.Create(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
zw := zip.NewWriter(f)
|
|
defer zw.Close()
|
|
|
|
w, err := zw.Create("word/document.xml")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = w.Write(documentXML)
|
|
return err
|
|
}
|