all repos

clerk @ 1efa5a1

missing tooling for ledger/hledger

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

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
lsp: go to definition, 1 month 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
			rj, err := l.ResolveFS(fsys, "in.journal")
46
			if err != nil {
47
				t.Fatalf("failed to load test journal: %v", err)
48
			}
49
50
			ctx := Build(rj)
51
			var b strings.Builder
52
			fprint(&b, ctx)
53
			golden.Assert(t, a, b.String())
54
		})
55
	}
56
}
57
58
func fprint(w io.Writer, a *Analysis) {
59
	fmt.Fprintf(w, "files (%d):\n", len(a.Files))
60
	for i, pf := range a.Files {
61
		fmt.Fprintf(w, "  %d: %s\n", i, filepath.Base(pf.Path))
62
	}
63
64
	// commodities
65
	csyms := make([]string, 0, len(a.Commodities))
66
	for sym := range a.Commodities {
67
		csyms = append(csyms, sym)
68
	}
69
	sort.Strings(csyms)
70
71
	fmt.Fprintf(w, "\ncommodities (%d):\n", len(a.Commodities))
72
	for _, sym := range csyms {
73
		info := a.Commodities[sym]
74
		fmt.Fprintf(w, "  %s\n", sym)
75
		fmt.Fprintf(w, "    directives: %d\n", len(info.Directives))
76
		fmt.Fprintf(w, "    used: %d\n", info.UsedCount)
77
		for _, cd := range info.Directives {
78
			fmt.Fprintf(w, "      commodity %s\n", cd.Commodity)
79
		}
80
		fmt.Fprintf(w, "    usages: %d\n", len(info.Usages))
81
		for _, u := range info.Usages {
82
			fmt.Fprintf(w, "      file %d: %s (%q)\n", u.FileIndex, u.Amount.Commodity, u.Amount.Quantity.String())
83
		}
84
		if info.LastUsed.Year != 0 {
85
			fmt.Fprintf(w, "    last-used: %d-%02d-%02d\n", info.LastUsed.Year, info.LastUsed.Month, info.LastUsed.Day)
86
		}
87
	}
88
89
	// accounts
90
	anames := make([]string, 0, len(a.Accounts))
91
	for name := range a.Accounts {
92
		anames = append(anames, name)
93
	}
94
	sort.Strings(anames)
95
96
	fmt.Fprintf(w, "\naccounts (%d):\n", len(a.Accounts))
97
	for _, name := range anames {
98
		info := a.Accounts[name]
99
		fmt.Fprintf(w, "  %s\n", name)
100
		fmt.Fprintf(w, "    directives: %d\n", len(info.Directives))
101
		fmt.Fprintf(w, "    used: %d\n", info.UsedCount)
102
		for _, d := range info.Directives {
103
			fmt.Fprintf(w, "      account %s\n", d.Account.String())
104
		}
105
		fmt.Fprintf(w, "    usages: %d\n", len(info.Usages))
106
		for _, u := range info.Usages {
107
			fmt.Fprintf(w, "      file %d: %s\n", u.FileIndex, u.Posting.Account.String())
108
		}
109
		if info.LastUsed.Year != 0 {
110
			fmt.Fprintf(w, "    last-used: %d-%02d-%02d\n", info.LastUsed.Year, info.LastUsed.Month, info.LastUsed.Day)
111
		}
112
	}
113
114
	// payees
115
	pnames := make([]string, 0, len(a.Payees))
116
	for name := range a.Payees {
117
		pnames = append(pnames, name)
118
	}
119
	sort.Strings(pnames)
120
121
	fmt.Fprintf(w, "\npayees (%d):\n", len(a.Payees))
122
	for _, name := range pnames {
123
		info := a.Payees[name]
124
		fmt.Fprintf(w, "  %s\n", name)
125
		fmt.Fprintf(w, "    directives: %d\n", len(info.Directives))
126
		fmt.Fprintf(w, "    used: %d\n", info.UsedCount)
127
		for _, d := range info.Directives {
128
			if d.Name != nil {
129
				fmt.Fprintf(w, "      payee %s\n", d.Name.Name)
130
			}
131
		}
132
		fmt.Fprintf(w, "    usages: %d\n", len(info.Usage))
133
		for _, u := range info.Usage {
134
			fmt.Fprintf(w, "      file %d: %s\n", u.FileIndex, u.Payee.Name)
135
		}
136
		if info.LastUsed.Year != 0 {
137
			fmt.Fprintf(w, "    last-used: %d-%02d-%02d\n", info.LastUsed.Year, info.LastUsed.Month, info.LastUsed.Day)
138
		}
139
	}
140
141
	// prefix index
142
	prefixes := make([]string, 0, len(a.AccountsByPrefix))
143
	for p := range a.AccountsByPrefix {
144
		prefixes = append(prefixes, p)
145
	}
146
	sort.Strings(prefixes)
147
	fmt.Fprintf(w, "\nprefixes (%d):\n", len(a.AccountsByPrefix))
148
	for _, p := range prefixes {
149
		names := a.AccountsByPrefix[p]
150
		sort.Strings(names)
151
		fmt.Fprintf(w, "  %s\n", p)
152
		for _, n := range names {
153
			fmt.Fprintf(w, "    %s\n", n)
154
		}
155
	}
156
157
	// typed entry counts
158
	fmt.Fprintf(w, "\ntransactions: %d\n", len(a.Transactions))
159
	fmt.Fprintf(w, "periodic transactions: %d\n", len(a.PeriodicTransactions))
160
	fmt.Fprintf(w, "automated transactions: %d\n", len(a.AutomatedTransactions))
161
162
	// directives
163
	fmt.Fprintf(w, "\ndirectives (%d):\n", len(a.Directives))
164
	for _, d := range a.Directives {
165
		switch d := d.(type) {
166
		case *ast.AccountDirective:
167
			fmt.Fprintf(w, "  account %s\n", d.Account.String())
168
		case *ast.CommodityDirective:
169
			fmt.Fprintf(w, "  commodity %s\n", d.Commodity)
170
		case *ast.PayeeDirective:
171
			if d.Name != nil {
172
				fmt.Fprintf(w, "  payee %s\n", d.Name.Name)
173
			}
174
		case *ast.TagDirective:
175
			fmt.Fprintf(w, "  tag %s\n", d.Name)
176
		case *ast.IncludeDirective:
177
			fmt.Fprintf(w, "  include %s\n", d.Path)
178
		case *ast.AliasDirective:
179
			fmt.Fprintf(w, "  alias %s = %s\n", d.From.String(), d.To.String())
180
		case *ast.YearDirective:
181
			fmt.Fprintf(w, "  year %d\n", d.Year)
182
		case *ast.DecimalMarkDirective:
183
			fmt.Fprintf(w, "  decimal-mark %c\n", d.Mark)
184
		case *ast.DefaultCommodityDirective:
185
			fmt.Fprintf(w, "  D %s%s\n", d.Amount.Commodity, d.Amount.Quantity.String())
186
		case *ast.MarketPriceDirective:
187
			fmt.Fprintf(w, "  P %s %s\n", d.Commodity, d.Amount.Quantity.String())
188
		case *ast.ConversionDirective:
189
			fmt.Fprintf(w, "  C %s%s @@ %s%s\n", d.From.Commodity, d.From.Quantity.String(), d.To.Commodity, d.To.Quantity.String())
190
		case *ast.ApplyDirective:
191
			fmt.Fprintf(w, "  apply %s\n", d.Expr)
192
		case *ast.EndDirective:
193
			fmt.Fprintf(w, "  end %s\n", d.Expr)
194
		case *ast.CommentBlockDirective:
195
			fmt.Fprintf(w, "  comment\n")
196
		case *ast.IgnoredDirective:
197
			fmt.Fprintf(w, "  N %s\n", d.Text)
198
		}
199
	}
200
201
	// transaction duplicate keys
202
	keys := make([]string, 0, len(a.TransactionsByKey))
203
	for k := range a.TransactionsByKey {
204
		keys = append(keys, k)
205
	}
206
	sort.Strings(keys)
207
	fmt.Fprintf(w, "\ntransaction keys (%d):\n", len(a.TransactionsByKey))
208
	for _, k := range keys {
209
		txs := a.TransactionsByKey[k]
210
		if len(txs) > 1 {
211
			fmt.Fprintf(w, "  %s (dup: %d)\n", k, len(txs))
212
		} else {
213
			fmt.Fprintf(w, "  %s\n", k)
214
		}
215
	}
216
217
	// payee templates
218
	payeeNames := make([]string, 0, len(a.PayeeTemplates))
219
	for name := range a.PayeeTemplates {
220
		payeeNames = append(payeeNames, name)
221
	}
222
	sort.Strings(payeeNames)
223
	fmt.Fprintf(w, "\npayee templates (%d):\n", len(a.PayeeTemplates))
224
	for _, name := range payeeNames {
225
		templates := a.PayeeTemplates[name]
226
		fmt.Fprintf(w, "  %s\n", name)
227
		for _, t := range templates {
228
			fmt.Fprintf(w, "    %s", t.Account)
229
			if t.Amount != "" {
230
				fmt.Fprintf(w, "  %s%s", t.Commodity, t.Amount)
231
			}
232
			if t.IsInferred {
233
				fmt.Fprintf(w, " (inferred)")
234
			}
235
			fmt.Fprintln(w)
236
		}
237
	}
238
239
	// dates
240
	fmt.Fprintf(w, "\ndates (%d):\n", len(a.Dates))
241
	for _, d := range a.Dates {
242
		fmt.Fprintf(w, "  %d-%02d-%02d\n", d.Year, d.Month, d.Day)
243
	}
244
245
	// tags
246
	tnames := make([]string, 0, len(a.Tags))
247
	for name := range a.Tags {
248
		tnames = append(tnames, name)
249
	}
250
	sort.Strings(tnames)
251
252
	fmt.Fprintf(w, "\ntags (%d):\n", len(a.Tags))
253
	for _, name := range tnames {
254
		info := a.Tags[name]
255
		fmt.Fprintf(w, "  %s\n", name)
256
		fmt.Fprintf(w, "    directives: %d\n", len(info.Directives))
257
		fmt.Fprintf(w, "    used: %d\n", info.UsedCount)
258
		for _, d := range info.Directives {
259
			fmt.Fprintf(w, "      tag %s\n", d.Name)
260
		}
261
		fmt.Fprintf(w, "    usages: %d\n", len(info.Usage))
262
		for _, u := range info.Usage {
263
			fmt.Fprintf(w, "      file %d: %s=%q\n", u.FileIndex, u.Tag.Key, u.Tag.Value)
264
		}
265
		if len(info.Values) > 0 {
266
			fmt.Fprintf(w, "    values: %s\n", strings.Join(info.Values, " "))
267
		}
268
		if info.LastUsed.Year != 0 {
269
			fmt.Fprintf(w, "    last-used: %d-%02d-%02d\n", info.LastUsed.Year, info.LastUsed.Month, info.LastUsed.Day)
270
		}
271
	}
272
273
	// tag names
274
	fmt.Fprintf(w, "\ntag names (%d):\n", len(a.TagNames))
275
	for _, t := range a.TagNames {
276
		fmt.Fprintf(w, "  %s\n", t)
277
	}
278
279
	// tag values
280
	fmt.Fprintf(w, "\ntag values (%d):\n", len(a.TagValues))
281
	for _, v := range a.TagValues {
282
		fmt.Fprintf(w, "  %s\n", v)
283
	}
284
285
	// commodity decimal marks
286
	csyms2 := make([]string, 0, len(a.CommodityDecimalMarks))
287
	for sym := range a.CommodityDecimalMarks {
288
		csyms2 = append(csyms2, sym)
289
	}
290
	sort.Strings(csyms2)
291
	fmt.Fprintf(w, "\ncommodity decimal marks (%d):\n", len(a.CommodityDecimalMarks))
292
	for _, sym := range csyms2 {
293
		fmt.Fprintf(w, "  %s: %c\n", sym, a.CommodityDecimalMarks[sym])
294
	}
295
}