generated from terraform-linters/tflint-ruleset-template
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main_test.go
287 lines (241 loc) · 7.69 KB
/
main_test.go
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
// main_test.go
package main
import (
"bytes"
"fmt"
"os"
"path/filepath"
"reflect"
"sort"
"strings"
"testing"
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
)
func TestRulesLength(t *testing.T) {
ruleSet := createRuleSet()
actualRules := len(ruleSet.Rules)
// Count .go files in rules directory that don't end with _test.go
rulesPath := "./rules"
expectedRules := 0
err := filepath.Walk(rulesPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() &&
strings.HasSuffix(path, ".go") &&
!strings.HasSuffix(path, "_test.go") {
expectedRules++
}
return nil
})
if err != nil {
t.Fatalf("Error walking rules directory: %v", err)
}
if actualRules != expectedRules {
t.Errorf("Number of rules does not match number of rule files. Got %d rules, expected %d",
actualRules, expectedRules)
}
}
func TestRuleLinks(t *testing.T) {
ruleSet := createRuleSet()
for _, rule := range ruleSet.Rules {
link := rule.Link()
if link == "" {
t.Errorf("Rule %T has an empty Link() return value", rule)
}
}
}
func TestRuleDocumentation(t *testing.T) {
ruleSet := createRuleSet()
docsDir := "./docs/rules"
templatePath := filepath.Join(docsDir, "template.md")
// Ensure docs directory exists
if err := os.MkdirAll(docsDir, 0755); err != nil {
t.Fatalf("Failed to create docs directory: %v", err)
}
// Read template file
templateContent, err := os.ReadFile(templatePath)
if err != nil {
t.Fatalf("Failed to read template file: %v", err)
}
for _, rule := range ruleSet.Rules {
ruleName := rule.Name()
docPath := filepath.Join(docsDir, ruleName+".md")
// Check if documentation file exists
fileInfo, err := os.Stat(docPath)
if os.IsNotExist(err) {
// Get resource type using reflection
ruleValue := reflect.ValueOf(rule)
resourceTypeField := ruleValue.Elem().FieldByName("resourceType")
if !resourceTypeField.IsValid() {
t.Errorf("Rule %s does not have a resourceType field", ruleName)
continue
}
resourceType := resourceTypeField.String()
// Prepare the content with replacements
content := string(templateContent)
replacements := map[string]string{
"{{rule_name}}": ruleName,
"{{severity}}": rule.Severity().String(),
"{{resource_name}}": resourceType,
}
for placeholder, value := range replacements {
content = strings.ReplaceAll(content, placeholder, value)
}
// Create documentation file with replaced content
err = os.WriteFile(docPath, []byte(content), 0644)
if err != nil {
t.Errorf("Failed to create documentation file for rule %s: %v", ruleName, err)
continue
}
// Verify the replacements were made correctly
createdContent, err := os.ReadFile(docPath)
if err != nil {
t.Errorf("Failed to read created documentation file for rule %s: %v", ruleName, err)
continue
}
// Check if any placeholders remain
for placeholder := range replacements {
if bytes.Contains(createdContent, []byte(placeholder)) {
t.Errorf("Documentation file for rule %s still contains placeholder %s", ruleName, placeholder)
}
}
} else if err != nil {
t.Errorf("Error checking documentation file for rule %s: %v", ruleName, err)
continue
} else {
// File exists, check for TODO markers
content, err := os.ReadFile(docPath)
if err != nil {
t.Errorf("Failed to read existing documentation file for rule %s: %v", ruleName, err)
continue
}
contentStr := string(content)
if strings.Contains(contentStr, "TODO") {
t.Errorf("Documentation file for rule %s still contains TODO markers. This indicates incomplete documentation", ruleName)
// Optional: Print the lines containing TODO for easier identification
lines := strings.Split(contentStr, "\n")
for i, line := range lines {
if strings.Contains(line, "TODO") {
t.Errorf(" Line %d: %s", i+1, line)
}
}
}
// Also verify the file is not empty
if fileInfo.Size() == 0 {
t.Errorf("Documentation file for rule %s exists but is empty", ruleName)
}
}
}
}
// main_test.go
func TestRulesReadme(t *testing.T) {
ruleSet := createRuleSet()
readmePath := "./docs/README.md"
// Create rules map grouped by resource type
rulesByType := make(map[string][]tflint.Rule)
var allRules []tflint.Rule
// Group rules by resource type and collect all rules for the table
for _, rule := range ruleSet.Rules {
ruleValue := reflect.ValueOf(rule)
resourceTypeField := ruleValue.Elem().FieldByName("resourceType")
if !resourceTypeField.IsValid() {
t.Errorf("Rule %s does not have a resourceType field", rule.Name())
continue
}
resourceType := resourceTypeField.String()
rulesByType[resourceType] = append(rulesByType[resourceType], rule)
allRules = append(allRules, rule)
}
// Sort all rules by name for the table
sort.Slice(allRules, func(i, j int) bool {
return allRules[i].Name() < allRules[j].Name()
})
// Sort resource types
var resourceTypes []string
for resourceType := range rulesByType {
resourceTypes = append(resourceTypes, resourceType)
}
sort.Strings(resourceTypes)
// Read existing README content
var existingContent string
existingContentBytes, err := os.ReadFile(readmePath)
if err == nil {
existingContent = string(existingContentBytes)
}
// Generate new content
var content strings.Builder
content.WriteString("# Rules\n\n")
// Create rules table
content.WriteString("## Rules Index\n\n")
content.WriteString("|Name|Severity|Enabled|\n")
content.WriteString("| --- | --- | --- |\n")
// Generate table entries
for _, rule := range allRules {
enabled := ""
if rule.Enabled() {
enabled = "✔"
}
content.WriteString(fmt.Sprintf("|[%s](./rules/%s.md)|%s|%s|\n",
rule.Name(),
rule.Name(),
rule.Severity().String(),
enabled))
}
content.WriteString("\n")
// Add resource type sections
content.WriteString("## Rules by Resource\n\n")
for _, resourceType := range resourceTypes {
content.WriteString(fmt.Sprintf("### %s\n\n", resourceType))
rules := rulesByType[resourceType]
// Sort rules within each resource type
sort.Slice(rules, func(i, j int) bool {
return rules[i].Name() < rules[j].Name()
})
for _, rule := range rules {
content.WriteString(fmt.Sprintf("- [%s](./rules/%s.md)\n", rule.Name(), rule.Name()))
}
content.WriteString("\n")
}
// Check if content needs to be updated
if existingContent != content.String() {
err := os.WriteFile(readmePath, []byte(content.String()), 0644)
if err != nil {
t.Fatalf("Failed to write README.md: %v", err)
}
t.Log("Updated rules/README.md with new content")
}
// Verify the content structure
currentContent, err := os.ReadFile(readmePath)
if err != nil {
t.Fatalf("Failed to read README.md for verification: %v", err)
}
// Verify required sections exist
requiredSections := []string{
"# Rules",
"## Rules Index",
"## Rules by Resource",
}
for _, section := range requiredSections {
if !strings.Contains(string(currentContent), section) {
t.Errorf("README.md is missing required section: %s", section)
}
}
// Verify table structure
if !strings.Contains(string(currentContent), "|Name|Severity|Enabled|") {
t.Error("README.md is missing required table header")
}
// Verify all rules are included
for _, rule := range allRules {
ruleName := rule.Name()
if !strings.Contains(string(currentContent), fmt.Sprintf("[%s](./rules/%s.md)", ruleName, ruleName)) {
t.Errorf("README.md is missing rule: %s", ruleName)
}
}
// Verify resource type sections
for _, resourceType := range resourceTypes {
if !strings.Contains(string(currentContent), fmt.Sprintf("### %s", resourceType)) {
t.Errorf("README.md is missing resource type section: %s", resourceType)
}
}
}