File: fn.go

package info (click to toggle)
golang-github-olekukonko-tablewriter 1.0.9-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental, forky, sid
  • size: 1,380 kB
  • sloc: makefile: 4
file content (317 lines) | stat: -rw-r--r-- 9,883 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
package tests

import (
	"bytes"
	"encoding/json"
	"fmt"
	"regexp"
	"strings"
	"testing"
	"unicode"
)

// mismatch represents a discrepancy between expected and actual output lines in a test.
type mismatch struct {
	Line     int    `json:"line"`     // Line number (1-based)
	Expected string `json:"expected"` // Expected line content and length
	Got      string `json:"got"`      // Actual line content and length
}

// MaskEmail masks email addresses in a slice of strings, replacing all but the first character of the local part with asterisks.
func MaskEmail(cells []string) []string {
	for i, cell := range cells {
		if strings.Contains(cell, "@") {
			parts := strings.Split(cell, "@")
			if len(parts) == 2 {
				masked := parts[0][:1] + strings.Repeat("*", len(parts[0])-1) + "@" + parts[1]
				cells[i] = masked
			}
		}
	}
	return cells
}

// MaskPassword masks strings that resemble passwords (containing "pass" or 8+ characters) with asterisks.
func MaskPassword(cells []string) []string {
	for i, cell := range cells {
		if len(cell) > 0 && (strings.Contains(strings.ToLower(cell), "pass") || len(cell) >= 8) {
			cells[i] = strings.Repeat("*", len(cell))
		}
	}
	return cells
}

// MaskCard masks credit card-like numbers, keeping only the last four digits visible.
func MaskCard(cells []string) []string {
	for i, cell := range cells {
		// Check for card-like numbers (12+ digits, with or without dashes/spaces)
		if len(cell) >= 12 && (strings.Contains(cell, "-") || len(strings.ReplaceAll(cell, " ", "")) >= 12) {
			parts := strings.FieldsFunc(cell, func(r rune) bool { return r == '-' || r == ' ' })
			masked := ""
			for j, part := range parts {
				if j < len(parts)-1 {
					masked += strings.Repeat("*", len(part))
				} else {
					masked += part // Keep last 4 digits visible
				}
				if j < len(parts)-1 {
					masked += "-"
				}
			}
			cells[i] = masked
		}
	}
	return cells
}

// visualCheck compares rendered output against expected lines, reporting mismatches in a test.
// It normalizes line endings, strips ANSI colors, and trims empty lines before comparison.
func visualCheck(t *testing.T, name string, output string, expected string) bool {
	t.Helper()

	// Normalize line endings and split into lines
	normalize := func(s string) []string {
		s = strings.ReplaceAll(s, "\r\n", "\n")
		s = StripColors(s)
		return strings.Split(s, "\n")
	}

	expectedLines := normalize(expected)
	outputLines := normalize(output)

	// Trim empty lines from start and end
	trimEmpty := func(lines []string) []string {
		start, end := 0, len(lines)
		for start < end && strings.TrimSpace(lines[start]) == "" {
			start++
		}
		for end > start && strings.TrimSpace(lines[end-1]) == "" {
			end--
		}
		return lines[start:end]
	}

	expectedLines = trimEmpty(expectedLines)
	outputLines = trimEmpty(outputLines)

	// Check line counts
	if len(outputLines) != len(expectedLines) {
		ex := strings.Join(expectedLines, "\n")
		ot := strings.Join(outputLines, "\n")
		t.Errorf("%s: line count mismatch - expected %d, got %d", name, len(expectedLines), len(outputLines))
		t.Errorf("Expected:\n%s\n", ex)
		t.Errorf("Got:\n%s\n", ot)
		return false
	}

	var mismatches []mismatch
	for i := 0; i < len(expectedLines) && i < len(outputLines); i++ {
		exp := strings.TrimSpace(expectedLines[i])
		got := strings.TrimSpace(outputLines[i])
		if exp != got {
			mismatches = append(mismatches, mismatch{
				Line:     i + 1,
				Expected: fmt.Sprintf("%s (%d)", exp, len(exp)),
				Got:      fmt.Sprintf("%s (%d)", got, len(got)),
			})
		}
	}

	// Report mismatches
	if len(mismatches) > 0 {
		diff, _ := json.MarshalIndent(mismatches, "", "  ")
		t.Errorf("%s: %d mismatches found:\n%s", name, len(mismatches), diff)
		t.Errorf("Full expected output:\n%s", expected)
		t.Errorf("Full actual output:\n%s", output)
		return false
	}

	return true
}

// visualCheckHTML compares rendered HTML output against expected lines,
// trimming whitespace per line and ignoring blank lines.
func visualCheckHTML(t *testing.T, name string, output string, expected string) bool {
	t.Helper()

	normalizeHTML := func(s string) []string {
		s = strings.ReplaceAll(s, "\r\n", "\n") // Normalize line endings
		lines := strings.Split(s, "\n")
		trimmedLines := make([]string, 0, len(lines))
		for _, line := range lines {
			trimmed := strings.TrimSpace(line)
			if trimmed != "" { // Only keep non-blank lines
				trimmedLines = append(trimmedLines, trimmed)
			}
		}
		return trimmedLines
	}

	expectedLines := normalizeHTML(expected)
	outputLines := normalizeHTML(output)

	// Compare line counts
	if len(outputLines) != len(expectedLines) {
		t.Errorf("%s: line count mismatch - expected %d, got %d", name, len(expectedLines), len(outputLines))
		t.Errorf("Expected (trimmed):\n%s", strings.Join(expectedLines, "\n"))
		t.Errorf("Got (trimmed):\n%s", strings.Join(outputLines, "\n"))
		// Optionally print full untrimmed for debugging exact whitespace
		// t.Errorf("Full Expected:\n%s", expected)
		// t.Errorf("Full Got:\n%s", output)
		return false
	}

	// Compare each line
	mismatches := []mismatch{} // Use mismatch struct from fn.go
	for i := 0; i < len(expectedLines); i++ {
		if expectedLines[i] != outputLines[i] {
			mismatches = append(mismatches, mismatch{
				Line:     i + 1,
				Expected: expectedLines[i],
				Got:      outputLines[i],
			})
		}
	}

	if len(mismatches) > 0 {
		t.Errorf("%s: %d mismatches found:", name, len(mismatches))
		for _, mm := range mismatches {
			t.Errorf("  Line %d:\n    Expected: %s\n    Got:      %s", mm.Line, mm.Expected, mm.Got)
		}
		// Optionally print full outputs again on mismatch
		// t.Errorf("Full Expected:\n%s", expected)
		// t.Errorf("Full Got:\n%s", output)
		return false
	}

	return true
}

// ansiColorRegex matches ANSI color escape sequences.
var ansiColorRegex = regexp.MustCompile(`\x1b\[[0-9;]*m`)

// StripColors removes ANSI color codes from a string.
func StripColors(s string) string {
	return ansiColorRegex.ReplaceAllString(s, "")
}

// Regex to remove leading/trailing whitespace from lines AND blank lines for HTML comparison
var htmlWhitespaceRegex = regexp.MustCompile(`(?m)^\s+|\s+$`)
var blankLineRegex = regexp.MustCompile(`(?m)^\s*\n`)

func normalizeHTMLStrict(s string) string {
	s = strings.ReplaceAll(s, "\r\n", "\n")
	s = strings.ReplaceAll(s, "\n", "") // Remove all newlines
	s = strings.ReplaceAll(s, "\t", "") // Remove tabs

	// Remove spaces after > and before <, effectively compacting tags
	s = regexp.MustCompile(`>\s+`).ReplaceAllString(s, ">")
	s = regexp.MustCompile(`\s+<`).ReplaceAllString(s, "<")

	// Trim overall leading/trailing space that might be left.
	return strings.TrimSpace(s)
}

// visualCheckCaption (helper function, potentially shared or adapted from your existing visualCheck)
// Ensure this helper normalizes expected and got strings for reliable comparison
// (e.g., trim spaces from each line, normalize newlines)
func visualCheckCaption(t *testing.T, testName, got, expected string) bool {
	t.Helper()
	normalize := func(s string) string {
		s = strings.ReplaceAll(s, "\r\n", "\n") // Normalize newlines
		lines := strings.Split(s, "\n")
		var trimmedLines []string
		for _, l := range lines {
			trimmedLines = append(trimmedLines, strings.TrimSpace(l))
		}
		// Join, then trim overall to handle cases where expected might have leading/trailing blank lines
		// but individual lines should keep their relative structure.
		return strings.TrimSpace(strings.Join(trimmedLines, "\n"))
	}

	gotNormalized := normalize(got)
	expectedNormalized := normalize(expected)

	if gotNormalized != expectedNormalized {
		// Use a more detailed diff output if available, or just print both.
		t.Errorf("%s: outputs do not match.\nExpected:\n```\n%s\n```\nGot:\n```\n%s\n```\n---Diff---\n%s",
			testName, expected, got, getDiff(expectedNormalized, gotNormalized)) // You might need a diff utility
		return false
	}
	return true
}

// A simple diff helper (replace with a proper library if needed)
func getDiff(expected, actual string) string {
	expectedLines := strings.Split(expected, "\n")
	actualLines := strings.Split(actual, "\n")
	maxLen := len(expectedLines)
	if len(actualLines) > maxLen {
		maxLen = len(actualLines)
	}
	var diff strings.Builder
	diff.WriteString("Line | Expected                         | Actual\n")
	diff.WriteString("-----|----------------------------------|----------------------------------\n")
	for i := 0; i < maxLen; i++ {
		eLine := ""
		if i < len(expectedLines) {
			eLine = expectedLines[i]
		}
		aLine := ""
		if i < len(actualLines) {
			aLine = actualLines[i]
		}
		marker := " "
		if eLine != aLine {
			marker = "!"
		}
		diff.WriteString(fmt.Sprintf("%4d %s| %-32s | %-32s\n", i+1, marker, eLine, aLine))
	}
	return diff.String()
}

func getLastContentLine(buf *bytes.Buffer) string {
	content := buf.String()
	lines := strings.Split(content, "\n")

	// Search backwards for first non-border, non-empty line
	for i := len(lines) - 1; i >= 0; i-- {
		line := strings.TrimSpace(lines[i])
		if line == "" || strings.Contains(line, "─") ||
			strings.Contains(line, "┌") || strings.Contains(line, "└") {
			continue
		}
		return line
	}
	return ""
}

type Name struct {
	First string
	Last  string
}

// this will be ignored since  Format() is present
func (n Name) String() string {
	return fmt.Sprintf("%s %s", n.First, n.Last)
}

// Note: Format() overrides String() if both exist.
func (n Name) Format() string {
	return fmt.Sprintf("%s %s", clean(n.First), clean(n.Last))
}

// clean ensures the first letter is capitalized and the rest are lowercase
func clean(s string) string {
	s = strings.TrimSpace(strings.ToLower(s))
	words := strings.Fields(s)
	s = strings.Join(words, "")

	if s == "" {
		return s
	}
	// Capitalize the first letter
	runes := []rune(s)
	runes[0] = unicode.ToUpper(runes[0])
	return string(runes)
}