all repos

clerk @ 78d7e880287a297d48d2667f5b4cd3448b4835ff

missing tooling for ledger/hledger

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

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