all repos

clerk @ e2e9e2b

missing tooling for ledger/hledger

clerk/internal/analyzer/analyzer_test.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
improve analyzer, 1 day ago
1
package analyzer
2
3
import (
4
	"fmt"
5
	"io"
6
	"path/filepath"
7
	"sort"
8
	"strings"
9
	"testing"
10
11
	"olexsmir.xyz/clerk/internal/testutil/golden"
12
	"olexsmir.xyz/clerk/journal"
13
	"olexsmir.xyz/clerk/journal/ast"
14
)
15
16
func TestAnalysis(t *testing.T) {
17
	tests := []string{
18
		"empty",
19
		"include",
20
		"journal",
21
		"typed-entries",
22
		"account-indexing",
23
		"account-usage",
24
		"commodity-usage",
25
		"payees",
26
		"periodic-automated",
27
		"directives",
28
		"cost-balance",
29
		"posting-types",
30
		"dates",
31
		"tags",
32
		"decimal-marks",
33
		"duplicate-transactions",
34
	}
35
36
	for _, tname := range tests {
37
		t.Run(tname, func(t *testing.T) {
38
			a := golden.Read(t, tname)
39
			fsys, err := a.FS()
40
			if err != nil {
41
				t.Fatal(err)
42
			}
43
44
			l := journal.NewLoader()
45
			if _, err := l.LoadFS(fsys, "in.journal"); err != nil {
46
				t.Fatalf("failed to load test journal: %v", err)
47
			}
48
49
			ctx := Build(l.Ordered())
50
			var b strings.Builder
51
			fprint(&b, ctx)
52
			golden.Assert(t, a, b.String())
53
		})
54
	}
55
}
56
57
func fprint(w io.Writer, a *Analysis) {
58
	fmt.Fprintf(w, "files (%d):\n", len(a.Files))
59
	for i, pf := range a.Files {
60
		fmt.Fprintf(w, "  %d: %s\n", i, filepath.Base(pf.Path))
61
	}
62
63
	// commodities
64
	csyms := make([]string, 0, len(a.Commodities))
65
	for sym := range a.Commodities {
66
		csyms = append(csyms, sym)
67
	}
68
	sort.Strings(csyms)
69
70
	fmt.Fprintf(w, "\ncommodities (%d):\n", len(a.Commodities))
71
	for _, sym := range csyms {
72
		info := a.Commodities[sym]
73
		fmt.Fprintf(w, "  %s\n", sym)
74
		fmt.Fprintf(w, "    directives: %d\n", len(info.Directives))
75
		fmt.Fprintf(w, "    used: %d\n", info.UsedCount)
76
		for _, cd := range info.Directives {
77
			fmt.Fprintf(w, "      commodity %s\n", cd.Commodity)
78
		}
79
		fmt.Fprintf(w, "    usages: %d\n", len(info.Usages))
80
		for _, u := range info.Usages {
81
			fmt.Fprintf(w, "      file %d: %s (%q)\n", u.FileIndex, u.Amount.Commodity, u.Amount.Quantity.String())
82
		}
83
		if info.LastUsed.Year != 0 {
84
			fmt.Fprintf(w, "    last-used: %d-%02d-%02d\n", info.LastUsed.Year, info.LastUsed.Month, info.LastUsed.Day)
85
		}
86
	}
87
88
	// accounts
89
	anames := make([]string, 0, len(a.Accounts))
90
	for name := range a.Accounts {
91
		anames = append(anames, name)
92
	}
93
	sort.Strings(anames)
94
95
	fmt.Fprintf(w, "\naccounts (%d):\n", len(a.Accounts))
96
	for _, name := range anames {
97
		info := a.Accounts[name]
98
		fmt.Fprintf(w, "  %s\n", name)
99
		fmt.Fprintf(w, "    directives: %d\n", len(info.Directives))
100
		fmt.Fprintf(w, "    used: %d\n", info.UsedCount)
101
		for _, d := range info.Directives {
102
			fmt.Fprintf(w, "      account %s\n", d.Account.String())
103
		}
104
		fmt.Fprintf(w, "    usages: %d\n", len(info.Usages))
105
		for _, u := range info.Usages {
106
			fmt.Fprintf(w, "      file %d: %s\n", u.FileIndex, u.Posting.Account.String())
107
		}
108
		if info.LastUsed.Year != 0 {
109
			fmt.Fprintf(w, "    last-used: %d-%02d-%02d\n", info.LastUsed.Year, info.LastUsed.Month, info.LastUsed.Day)
110
		}
111
	}
112
113
	// payees
114
	pnames := make([]string, 0, len(a.Payees))
115
	for name := range a.Payees {
116
		pnames = append(pnames, name)
117
	}
118
	sort.Strings(pnames)
119
120
	fmt.Fprintf(w, "\npayees (%d):\n", len(a.Payees))
121
	for _, name := range pnames {
122
		info := a.Payees[name]
123
		fmt.Fprintf(w, "  %s\n", name)
124
		fmt.Fprintf(w, "    directives: %d\n", len(info.Directives))
125
		fmt.Fprintf(w, "    used: %d\n", info.UsedCount)
126
		for _, d := range info.Directives {
127
			fmt.Fprintf(w, "      payee %s\n", d.Name)
128
		}
129
		fmt.Fprintf(w, "    usages: %d\n", len(info.Usage))
130
		for _, u := range info.Usage {
131
			fmt.Fprintf(w, "      file %d: %s\n", u.FileIndex, u.Payee.Name)
132
		}
133
		if info.LastUsed.Year != 0 {
134
			fmt.Fprintf(w, "    last-used: %d-%02d-%02d\n", info.LastUsed.Year, info.LastUsed.Month, info.LastUsed.Day)
135
		}
136
	}
137
138
	// prefix index
139
	prefixes := make([]string, 0, len(a.AccountsByPrefix))
140
	for p := range a.AccountsByPrefix {
141
		prefixes = append(prefixes, p)
142
	}
143
	sort.Strings(prefixes)
144
	fmt.Fprintf(w, "\nprefixes (%d):\n", len(a.AccountsByPrefix))
145
	for _, p := range prefixes {
146
		names := a.AccountsByPrefix[p]
147
		sort.Strings(names)
148
		fmt.Fprintf(w, "  %s\n", p)
149
		for _, n := range names {
150
			fmt.Fprintf(w, "    %s\n", n)
151
		}
152
	}
153
154
	// typed entry counts
155
	fmt.Fprintf(w, "\ntransactions: %d\n", len(a.Transactions))
156
	fmt.Fprintf(w, "periodic transactions: %d\n", len(a.PeriodicTransactions))
157
	fmt.Fprintf(w, "automated transactions: %d\n", len(a.AutomatedTransactions))
158
159
	// directives
160
	fmt.Fprintf(w, "\ndirectives (%d):\n", len(a.Directives))
161
	for _, d := range a.Directives {
162
		switch d := d.(type) {
163
		case *ast.AccountDirective:
164
			fmt.Fprintf(w, "  account %s\n", d.Account.String())
165
		case *ast.CommodityDirective:
166
			fmt.Fprintf(w, "  commodity %s\n", d.Commodity)
167
		case *ast.PayeeDirective:
168
			fmt.Fprintf(w, "  payee %s\n", d.Name)
169
		case *ast.TagDirective:
170
			fmt.Fprintf(w, "  tag %s\n", d.Name)
171
		case *ast.IncludeDirective:
172
			fmt.Fprintf(w, "  include %s\n", d.Path)
173
		case *ast.AliasDirective:
174
			fmt.Fprintf(w, "  alias %s = %s\n", d.From.String(), d.To.String())
175
		case *ast.YearDirective:
176
			fmt.Fprintf(w, "  year %d\n", d.Year)
177
		case *ast.DecimalMarkDirective:
178
			fmt.Fprintf(w, "  decimal-mark %c\n", d.Mark)
179
		case *ast.DefaultCommodityDirective:
180
			fmt.Fprintf(w, "  D %s%s\n", d.Amount.Commodity, d.Amount.Quantity.String())
181
		case *ast.MarketPriceDirective:
182
			fmt.Fprintf(w, "  P %s %s\n", d.Commodity, d.Amount.Quantity.String())
183
		case *ast.ConversionDirective:
184
			fmt.Fprintf(w, "  C %s%s @@ %s%s\n", d.From.Commodity, d.From.Quantity.String(), d.To.Commodity, d.To.Quantity.String())
185
		case *ast.ApplyDirective:
186
			fmt.Fprintf(w, "  apply %s\n", d.Expr)
187
		case *ast.EndDirective:
188
			fmt.Fprintf(w, "  end %s\n", d.Expr)
189
		case *ast.CommentBlockDirective:
190
			fmt.Fprintf(w, "  comment\n")
191
		case *ast.IgnoredDirective:
192
			fmt.Fprintf(w, "  N %s\n", d.Text)
193
		}
194
	}
195
196
	// transaction duplicate keys
197
	keys := make([]string, 0, len(a.TransactionsByKey))
198
	for k := range a.TransactionsByKey {
199
		keys = append(keys, k)
200
	}
201
	sort.Strings(keys)
202
	fmt.Fprintf(w, "\ntransaction keys (%d):\n", len(a.TransactionsByKey))
203
	for _, k := range keys {
204
		txs := a.TransactionsByKey[k]
205
		if len(txs) > 1 {
206
			fmt.Fprintf(w, "  %s (dup: %d)\n", k, len(txs))
207
		} else {
208
			fmt.Fprintf(w, "  %s\n", k)
209
		}
210
	}
211
212
	// payee templates
213
	payeeNames := make([]string, 0, len(a.PayeeTemplates))
214
	for name := range a.PayeeTemplates {
215
		payeeNames = append(payeeNames, name)
216
	}
217
	sort.Strings(payeeNames)
218
	fmt.Fprintf(w, "\npayee templates (%d):\n", len(a.PayeeTemplates))
219
	for _, name := range payeeNames {
220
		templates := a.PayeeTemplates[name]
221
		fmt.Fprintf(w, "  %s\n", name)
222
		for _, t := range templates {
223
			fmt.Fprintf(w, "    %s", t.Account)
224
			if t.Amount != "" {
225
				fmt.Fprintf(w, "  %s%s", t.Commodity, t.Amount)
226
			}
227
			if t.IsInferred {
228
				fmt.Fprintf(w, " (inferred)")
229
			}
230
			fmt.Fprintln(w)
231
		}
232
	}
233
234
	// dates
235
	fmt.Fprintf(w, "\ndates (%d):\n", len(a.Dates))
236
	for _, d := range a.Dates {
237
		fmt.Fprintf(w, "  %d-%02d-%02d\n", d.Year, d.Month, d.Day)
238
	}
239
240
	// tag names
241
	fmt.Fprintf(w, "\ntag names (%d):\n", len(a.TagNames))
242
	for _, t := range a.TagNames {
243
		fmt.Fprintf(w, "  %s\n", t)
244
	}
245
246
	// commodity decimal marks
247
	csyms2 := make([]string, 0, len(a.CommodityDecimalMarks))
248
	for sym := range a.CommodityDecimalMarks {
249
		csyms2 = append(csyms2, sym)
250
	}
251
	sort.Strings(csyms2)
252
	fmt.Fprintf(w, "\ncommodity decimal marks (%d):\n", len(a.CommodityDecimalMarks))
253
	for _, sym := range csyms2 {
254
		fmt.Fprintf(w, "  %s: %c\n", sym, a.CommodityDecimalMarks[sym])
255
	}
256
}