423 lines
8.5 KiB
Go
423 lines
8.5 KiB
Go
package ledger
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestFileAdd(t *testing.T) {
|
|
filesAppendDelim = " "
|
|
payee := "name:3"
|
|
delta := Delta{
|
|
Date: "2999-88-77",
|
|
Description: "66",
|
|
Name: "name:1",
|
|
Value: 2.00,
|
|
Currency: USD,
|
|
}
|
|
cases := map[string]struct {
|
|
given []byte
|
|
want string
|
|
}{
|
|
"no file": {
|
|
given: nil,
|
|
want: `
|
|
2999-88-77 66
|
|
name:1 $2.00
|
|
name:3`,
|
|
},
|
|
"empty file": {
|
|
given: []byte{},
|
|
want: `
|
|
2999-88-77 66
|
|
name:1 $2.00
|
|
name:3`,
|
|
},
|
|
"happy without trailing whitespace": {
|
|
given: []byte(`
|
|
2000-01-02 desc
|
|
name:1 $1.00
|
|
name:2 $-1.00`),
|
|
want: `
|
|
2000-01-02 desc
|
|
name:1 $1.00
|
|
name:2 $-1.00
|
|
2999-88-77 66
|
|
name:1 $2.00
|
|
name:3`,
|
|
},
|
|
"happy with trailing newline": {
|
|
given: []byte(`
|
|
2000-01-02 desc
|
|
name:1 $1.00
|
|
name:2 $-1.00
|
|
`),
|
|
want: `
|
|
2000-01-02 desc
|
|
name:1 $1.00
|
|
name:2 $-1.00
|
|
2999-88-77 66
|
|
name:1 $2.00
|
|
name:3`,
|
|
},
|
|
}
|
|
|
|
for name, d := range cases {
|
|
c := d
|
|
t.Run(name, func(t *testing.T) {
|
|
p := path.Join(t.TempDir(), "input")
|
|
if c.given != nil {
|
|
if err := os.WriteFile(p, []byte(c.given), os.ModePerm); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
f, err := NewFiles(p)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := f.Add(payee, delta); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if got, err := os.ReadFile(p); err != nil {
|
|
t.Fatal(err)
|
|
} else if string(got) != c.want {
|
|
t.Errorf("wanted\n\t%s, got\n\t%s", c.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFileTestdataMacroWithBPI(t *testing.T) {
|
|
paths, err := filepath.Glob("./testdata/macro.d/*")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Log(paths)
|
|
|
|
f, err := NewFiles(paths[0], paths[1:]...)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
deltas, err := f.Deltas()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
bpis, err := NewBPIs("./testdata/bpi.bpi")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
t.Run("bal like", func(t *testing.T) {
|
|
bal := deltas.Like(LikeName(`^AssetAccount:Bond`)).Balances().WithBPIs(bpis)
|
|
for k, v := range bal {
|
|
t.Logf("%s: %+v", k, v)
|
|
}
|
|
})
|
|
|
|
t.Run("reg like", func(t *testing.T) {
|
|
reg := deltas.Like(LikeName(`^AssetAccount:Bond`)).Register()
|
|
for k, v := range reg {
|
|
t.Logf("%s: %+v", k, v.WithBPIs(bpis))
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestFileTestdata(t *testing.T) {
|
|
t.Run("macro.d", func(t *testing.T) {
|
|
paths, err := filepath.Glob("./testdata/macro.d/*")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
f, err := NewFiles(paths[0], paths[1:]...)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
deltas, err := f.Deltas()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
t.Run("deltas", func(t *testing.T) {
|
|
for i := range deltas {
|
|
t.Logf("%+v", deltas[i].Debug())
|
|
}
|
|
})
|
|
|
|
t.Run("balances", func(t *testing.T) {
|
|
balances := deltas.Balances()
|
|
for k, v := range balances {
|
|
t.Logf("%s: %+v", k, v)
|
|
}
|
|
})
|
|
|
|
t.Run("balances like", func(t *testing.T) {
|
|
balances := deltas.Like(LikeName(`^AssetAccount:`)).Balances()
|
|
for k, v := range balances {
|
|
t.Logf("%s: %+v", k, v)
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("single files", func(t *testing.T) {
|
|
paths, err := filepath.Glob("./testdata/*.dat")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
for _, pathd := range paths {
|
|
path := pathd
|
|
t.Run(path, func(t *testing.T) {
|
|
f, err := NewFiles(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
deltas, err := f.Deltas()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
t.Run("deltas", func(t *testing.T) {
|
|
for i := range deltas {
|
|
t.Logf("%+v", deltas[i].Debug())
|
|
}
|
|
})
|
|
|
|
t.Run("balances", func(t *testing.T) {
|
|
balances := deltas.Balances()
|
|
for k, v := range balances {
|
|
t.Logf("%s: %+v", k, v)
|
|
}
|
|
})
|
|
|
|
t.Run("balances like", func(t *testing.T) {
|
|
balances := deltas.Like(LikeName(`AssetAccount:Cash:Fidelity76`)).Balances()
|
|
for k, v := range balances {
|
|
t.Logf("%s: %+v", k, v)
|
|
}
|
|
})
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestFileDeltas(t *testing.T) {
|
|
happy := []Delta{
|
|
{
|
|
Date: "2022-12-12",
|
|
Name: "AssetAccount:Cash:Fidelity76",
|
|
Value: -97.92,
|
|
Currency: USD,
|
|
Description: "Electricity / Power Bill TG2PJ-2PLP5",
|
|
},
|
|
{
|
|
Date: "2022-12-12",
|
|
Name: "Withdrawal:0:SharedHome:DominionEnergy",
|
|
Value: 97.92,
|
|
Currency: USD,
|
|
Description: "Electricity / Power Bill TG2PJ-2PLP5",
|
|
},
|
|
{
|
|
Date: "2022-12-12",
|
|
Name: "AssetAccount:Cash:Fidelity76",
|
|
Value: -1.00,
|
|
Currency: USD,
|
|
Description: "Test pay chase TG32S-BT2FF",
|
|
},
|
|
{
|
|
Date: "2022-12-12",
|
|
Name: "Debts:Credit:ChaseFreedomUltdVisa",
|
|
Value: 1.00,
|
|
Currency: USD,
|
|
Description: "Test pay chase TG32S-BT2FF",
|
|
},
|
|
}
|
|
|
|
cases := map[string][]Delta{
|
|
"empty": nil,
|
|
"one": happy[:2],
|
|
"happy": happy[:],
|
|
}
|
|
|
|
for name, d := range cases {
|
|
want := d
|
|
t.Run(name, func(t *testing.T) {
|
|
f, err := NewFiles("./testdata/" + name + ".dat")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
deltas, err := f.Deltas()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(deltas) != len(want) {
|
|
t.Error(len(deltas))
|
|
}
|
|
for i := range want {
|
|
if i >= len(deltas) {
|
|
break
|
|
}
|
|
if want[i] != deltas[i] {
|
|
t.Errorf("[%d] \n\twant=%s, \n\t got=%s", i, want[i].Debug(), deltas[i].Debug())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFilesOfDir(t *testing.T) {
|
|
d := t.TempDir()
|
|
files := Files([]string{d, "/dev/null"})
|
|
if paths := files.paths(); len(paths) != 1 {
|
|
t.Error(paths)
|
|
}
|
|
|
|
os.WriteFile(path.Join(d, "1"), []byte{}, os.ModePerm)
|
|
os.Mkdir(path.Join(d, "d2"), os.ModePerm)
|
|
os.WriteFile(path.Join(d, "d2", "2"), []byte{}, os.ModePerm)
|
|
if paths := files.paths(); len(paths) != 3 || paths[0] != path.Join(d, "1") || paths[1] != path.Join(d, "d2", "2") {
|
|
t.Error(paths)
|
|
}
|
|
}
|
|
|
|
func TestFilesTempGetLastNLines(t *testing.T) {
|
|
cases := map[string]struct {
|
|
input string
|
|
n int
|
|
want []string
|
|
}{
|
|
"empty": {},
|
|
"get n lines from empty file": {
|
|
input: "",
|
|
n: 5,
|
|
want: []string{},
|
|
},
|
|
"get 0 lines from file": {
|
|
input: "#a\n#b",
|
|
n: 0,
|
|
want: []string{},
|
|
},
|
|
"get 3 lines from 2 line file": {
|
|
input: "#a\n#b",
|
|
n: 3,
|
|
want: []string{"#a", "#b"},
|
|
},
|
|
"get 2 lines from 3 line file": {
|
|
input: "#a\n#b\n#c",
|
|
n: 2,
|
|
want: []string{"#b", "#c"},
|
|
},
|
|
}
|
|
|
|
for name, d := range cases {
|
|
c := d
|
|
t.Run(name, func(t *testing.T) {
|
|
p := path.Join(t.TempDir(), base64.URLEncoding.EncodeToString([]byte(t.Name())))
|
|
os.WriteFile(p, []byte(c.input), os.ModePerm)
|
|
files, err := NewFiles(p)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if got, err := files.TempGetLastNLines(c.n); err != nil {
|
|
t.Fatal(err)
|
|
} else if fmt.Sprint(got) != fmt.Sprint(c.want) {
|
|
for i := range c.want {
|
|
t.Logf("want[%d] = %q", i, c.want[i])
|
|
}
|
|
for i := range got {
|
|
t.Logf(" got[%d] = %q", i, got[i])
|
|
}
|
|
t.Errorf("wanted\n\t%+v, got\n\t%+v", c.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFilesTempSetLastNLines(t *testing.T) {
|
|
cases := map[string]struct {
|
|
given string
|
|
input []string
|
|
n int
|
|
want string
|
|
}{
|
|
"empty": {},
|
|
"append to empty": {
|
|
input: []string{"hello", "world"},
|
|
n: 100,
|
|
want: "hello\nworld\n",
|
|
},
|
|
"replace last 10 of 1 lines with 2": {
|
|
given: "ohno",
|
|
input: []string{"hello", "world"},
|
|
n: 10,
|
|
want: "hello\nworld\n",
|
|
},
|
|
"replace last 1 of 1 lines with 2": {
|
|
given: "ohno",
|
|
input: []string{"hello", "world"},
|
|
n: 1,
|
|
want: "hello\nworld\n",
|
|
},
|
|
"replace last 0 of 1 lines with 2": {
|
|
given: "ohno",
|
|
input: []string{"hello", "world"},
|
|
n: 0,
|
|
want: "ohno\nhello\nworld\n",
|
|
},
|
|
"replace last 1 of 1 lines with 0": {
|
|
given: "ohno",
|
|
input: []string{},
|
|
n: 1,
|
|
want: "",
|
|
},
|
|
"replace last 1 of 2 lines with 1": {
|
|
given: "ohno\nhaha",
|
|
input: []string{"replaced"},
|
|
n: 1,
|
|
want: "ohno\nreplaced\n",
|
|
},
|
|
"replace last 1 of 2 lines with 2": {
|
|
given: "ohno\nhaha",
|
|
input: []string{"replac", "ed"},
|
|
n: 1,
|
|
want: "ohno\nreplac\ned\n",
|
|
},
|
|
}
|
|
|
|
for name, d := range cases {
|
|
c := d
|
|
t.Run(name, func(t *testing.T) {
|
|
p := path.Join(t.TempDir(), base64.URLEncoding.EncodeToString([]byte(t.Name())))
|
|
os.WriteFile(p, []byte(c.given), os.ModePerm)
|
|
files := Files([]string{p})
|
|
if err := files.TempSetLastNLines(c.n, c.input); err != nil {
|
|
s := err.Error()
|
|
if _, err := os.Stat(s); err == nil {
|
|
got, _ := os.ReadFile(s)
|
|
if string(got) != c.want {
|
|
t.Errorf("want\n%s, got\n%s", c.want, got)
|
|
}
|
|
}
|
|
t.Fatal(err)
|
|
}
|
|
got, _ := os.ReadFile(p)
|
|
if string(got) != c.want {
|
|
t.Errorf("want\n%s, got\n%s", c.want, got)
|
|
}
|
|
})
|
|
}
|
|
}
|