aboutsummaryrefslogtreecommitdiffstats
path: root/internal/xls/xls_test.go
diff options
context:
space:
mode:
authorSimen A. W. Olsen <hello@simenandre.no>2026-10-27 17:19:57 +0100
committerSimen A. W. Olsen <hello@simenandre.no>2026-10-27 17:19:57 +0100
commit0571b30b6da27dc4c9e617403deab09b974ac641 (patch)
treef3dfab18e22a48d06c64e563ba9bcf478c01a5bc /internal/xls/xls_test.go
downloadaccounted-multicase-intgr-main.tar.gz
accounted-multicase-intgr-main.zip
feat: send Multicase buntfil vouchers to AccountedHEADmain
Diffstat (limited to 'internal/xls/xls_test.go')
-rw-r--r--internal/xls/xls_test.go99
1 files changed, 99 insertions, 0 deletions
diff --git a/internal/xls/xls_test.go b/internal/xls/xls_test.go
new file mode 100644
index 0000000..71ed21a
--- /dev/null
+++ b/internal/xls/xls_test.go
@@ -0,0 +1,99 @@
+package xls
+
+import (
+ "encoding/binary"
+ "math"
+ "reflect"
+ "testing"
+)
+
+func rkInt(n int32, div100 bool) uint32 {
+ rk := uint32(n)<<2 | 0x02
+ if div100 {
+ rk |= 0x01
+ }
+ return rk
+}
+
+func TestDecodeRK(t *testing.T) {
+ tests := []struct {
+ rk uint32
+ want float64
+ }{
+ {rkInt(1460, false), 1460},
+ {rkInt(-677083, true), -6770.83},
+ {rkInt(-1, false), -1},
+ {rkInt(4670, true), 46.7},
+ // Float form: the top 30 bits of an IEEE double.
+ {uint32(math.Float64bits(-11800) >> 32), -11800},
+ }
+ for _, tt := range tests {
+ if got := decodeRK(tt.rk); math.Abs(got-tt.want) > 1e-9 {
+ t.Errorf("decodeRK(%#x) = %v, want %v", tt.rk, got, tt.want)
+ }
+ }
+}
+
+type builder struct{ b []byte }
+
+func (w *builder) rec(typ uint16, data []byte) {
+ w.b = binary.LittleEndian.AppendUint16(w.b, typ)
+ w.b = binary.LittleEndian.AppendUint16(w.b, uint16(len(data)))
+ w.b = append(w.b, data...)
+}
+
+func cellHead(row, col uint16) []byte {
+ b := binary.LittleEndian.AppendUint16(nil, row)
+ b = binary.LittleEndian.AppendUint16(b, col)
+ return binary.LittleEndian.AppendUint16(b, 0) // xf
+}
+
+func TestParseWorkbook(t *testing.T) {
+ // SST with two strings; the second ("Bærum", stored as UTF-16) is split
+ // across a CONTINUE record, which restarts with its own option byte.
+ sst := binary.LittleEndian.AppendUint32(nil, 2)
+ sst = binary.LittleEndian.AppendUint32(sst, 2)
+ sst = append(sst, 5, 0, 0)
+ sst = append(sst, "Konto"...)
+ sst = append(sst, 5, 0, 1, 'B', 0, 0xE6, 0)
+ cont := []byte{0, 'r', 'u', 'm'}
+
+ var sheet builder
+ sheet.rec(recBOF, make([]byte, 16))
+ sheet.rec(recLabelSST, binary.LittleEndian.AppendUint32(cellHead(0, 0), 0))
+ sheet.rec(recLabelSST, binary.LittleEndian.AppendUint32(cellHead(0, 1), 1))
+ sheet.rec(recRK, binary.LittleEndian.AppendUint32(cellHead(1, 0), rkInt(1460, false)))
+ mulrk := binary.LittleEndian.AppendUint16(nil, 1)
+ mulrk = binary.LittleEndian.AppendUint16(mulrk, 1)
+ for _, v := range []uint32{rkInt(-677083, true), rkInt(5, false)} {
+ mulrk = binary.LittleEndian.AppendUint16(mulrk, 0)
+ mulrk = binary.LittleEndian.AppendUint32(mulrk, v)
+ }
+ mulrk = binary.LittleEndian.AppendUint16(mulrk, 2)
+ sheet.rec(recMulRK, mulrk)
+ sheet.rec(recNumber, binary.LittleEndian.AppendUint64(cellHead(2, 0), math.Float64bits(44587.125)))
+ sheet.rec(recEOF, nil)
+
+ // The globals substream, with the BOUNDSHEET offset patched once its
+ // own length is known.
+ var globals builder
+ globals.rec(recBOF, make([]byte, 16))
+ globals.rec(recBoundSheet, append(make([]byte, 6), 1, 0, 'S'))
+ globals.rec(recSST, sst)
+ globals.rec(recContinue, cont)
+ globals.rec(recEOF, nil)
+ binary.LittleEndian.PutUint32(globals.b[20+4:], uint32(len(globals.b)))
+
+ grid, err := ParseWorkbook(append(globals.b, sheet.b...))
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := [][]string{
+ {"Konto", "Bærum"},
+ {"1460", "-6770.83", "5"},
+ {"44587.125"},
+ }
+ if !reflect.DeepEqual(grid, want) {
+ t.Fatalf("grid = %q, want %q", grid, want)
+ }
+}