All checks were successful
* Introduce tests for PostgreSQL data types and keywords. * Implement tests for reflect utility functions. * Ensure consistency and correctness of type conversions and keyword mappings. * Validate behavior for various edge cases and input types.
367 lines
8.5 KiB
Go
367 lines
8.5 KiB
Go
package inspector
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func createTestReport() *InspectorReport {
|
|
return &InspectorReport{
|
|
Summary: ReportSummary{
|
|
TotalRules: 10,
|
|
RulesChecked: 8,
|
|
RulesSkipped: 2,
|
|
ErrorCount: 3,
|
|
WarningCount: 5,
|
|
PassedCount: 12,
|
|
},
|
|
Violations: []ValidationResult{
|
|
{
|
|
RuleName: "primary_key_naming",
|
|
Level: "error",
|
|
Message: "Primary key should start with 'id_'",
|
|
Location: "public.users.user_id",
|
|
Passed: false,
|
|
Context: map[string]interface{}{
|
|
"schema": "public",
|
|
"table": "users",
|
|
"column": "user_id",
|
|
"pattern": "^id_",
|
|
},
|
|
},
|
|
{
|
|
RuleName: "table_name_length",
|
|
Level: "warning",
|
|
Message: "Table name too long",
|
|
Location: "public.very_long_table_name_that_exceeds_limits",
|
|
Passed: false,
|
|
Context: map[string]interface{}{
|
|
"schema": "public",
|
|
"table": "very_long_table_name_that_exceeds_limits",
|
|
"length": 44,
|
|
"max_length": 32,
|
|
},
|
|
},
|
|
},
|
|
GeneratedAt: time.Now(),
|
|
Database: "testdb",
|
|
SourceFormat: "postgresql",
|
|
}
|
|
}
|
|
|
|
func TestNewMarkdownFormatter(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
formatter := NewMarkdownFormatter(&buf)
|
|
|
|
if formatter == nil {
|
|
t.Fatal("NewMarkdownFormatter() returned nil")
|
|
}
|
|
|
|
// Buffer is not a terminal, so colors should be disabled
|
|
if formatter.UseColors {
|
|
t.Error("NewMarkdownFormatter() UseColors should be false for non-terminal")
|
|
}
|
|
}
|
|
|
|
func TestNewJSONFormatter(t *testing.T) {
|
|
formatter := NewJSONFormatter()
|
|
|
|
if formatter == nil {
|
|
t.Fatal("NewJSONFormatter() returned nil")
|
|
}
|
|
}
|
|
|
|
func TestMarkdownFormatter_Format(t *testing.T) {
|
|
report := createTestReport()
|
|
var buf bytes.Buffer
|
|
formatter := NewMarkdownFormatter(&buf)
|
|
|
|
output, err := formatter.Format(report)
|
|
if err != nil {
|
|
t.Fatalf("MarkdownFormatter.Format() returned error: %v", err)
|
|
}
|
|
|
|
// Check that output contains expected sections
|
|
if !strings.Contains(output, "# RelSpec Inspector Report") {
|
|
t.Error("Markdown output missing header")
|
|
}
|
|
|
|
if !strings.Contains(output, "Database:") {
|
|
t.Error("Markdown output missing database field")
|
|
}
|
|
|
|
if !strings.Contains(output, "testdb") {
|
|
t.Error("Markdown output missing database name")
|
|
}
|
|
|
|
if !strings.Contains(output, "Summary") {
|
|
t.Error("Markdown output missing summary section")
|
|
}
|
|
|
|
if !strings.Contains(output, "Rules Checked: 8") {
|
|
t.Error("Markdown output missing rules checked count")
|
|
}
|
|
|
|
if !strings.Contains(output, "Errors: 3") {
|
|
t.Error("Markdown output missing error count")
|
|
}
|
|
|
|
if !strings.Contains(output, "Warnings: 5") {
|
|
t.Error("Markdown output missing warning count")
|
|
}
|
|
|
|
if !strings.Contains(output, "Violations") {
|
|
t.Error("Markdown output missing violations section")
|
|
}
|
|
|
|
if !strings.Contains(output, "primary_key_naming") {
|
|
t.Error("Markdown output missing rule name")
|
|
}
|
|
|
|
if !strings.Contains(output, "public.users.user_id") {
|
|
t.Error("Markdown output missing location")
|
|
}
|
|
}
|
|
|
|
func TestMarkdownFormatter_FormatNoViolations(t *testing.T) {
|
|
report := &InspectorReport{
|
|
Summary: ReportSummary{
|
|
TotalRules: 10,
|
|
RulesChecked: 10,
|
|
RulesSkipped: 0,
|
|
ErrorCount: 0,
|
|
WarningCount: 0,
|
|
PassedCount: 50,
|
|
},
|
|
Violations: []ValidationResult{},
|
|
GeneratedAt: time.Now(),
|
|
Database: "testdb",
|
|
SourceFormat: "postgresql",
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
formatter := NewMarkdownFormatter(&buf)
|
|
|
|
output, err := formatter.Format(report)
|
|
if err != nil {
|
|
t.Fatalf("MarkdownFormatter.Format() returned error: %v", err)
|
|
}
|
|
|
|
if !strings.Contains(output, "No violations found") {
|
|
t.Error("Markdown output should indicate no violations")
|
|
}
|
|
}
|
|
|
|
func TestJSONFormatter_Format(t *testing.T) {
|
|
report := createTestReport()
|
|
formatter := NewJSONFormatter()
|
|
|
|
output, err := formatter.Format(report)
|
|
if err != nil {
|
|
t.Fatalf("JSONFormatter.Format() returned error: %v", err)
|
|
}
|
|
|
|
// Verify it's valid JSON
|
|
var decoded InspectorReport
|
|
if err := json.Unmarshal([]byte(output), &decoded); err != nil {
|
|
t.Fatalf("JSONFormatter.Format() produced invalid JSON: %v", err)
|
|
}
|
|
|
|
// Check key fields
|
|
if decoded.Database != "testdb" {
|
|
t.Errorf("JSON decoded Database = %q, want \"testdb\"", decoded.Database)
|
|
}
|
|
|
|
if decoded.Summary.ErrorCount != 3 {
|
|
t.Errorf("JSON decoded ErrorCount = %d, want 3", decoded.Summary.ErrorCount)
|
|
}
|
|
|
|
if len(decoded.Violations) != 2 {
|
|
t.Errorf("JSON decoded Violations length = %d, want 2", len(decoded.Violations))
|
|
}
|
|
}
|
|
|
|
func TestMarkdownFormatter_FormatHeader(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
formatter := NewMarkdownFormatter(&buf)
|
|
|
|
header := formatter.formatHeader("Test Header")
|
|
|
|
if !strings.Contains(header, "# Test Header") {
|
|
t.Errorf("formatHeader() = %q, want to contain \"# Test Header\"", header)
|
|
}
|
|
}
|
|
|
|
func TestMarkdownFormatter_FormatBold(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
useColors bool
|
|
text string
|
|
wantContains string
|
|
}{
|
|
{
|
|
name: "without colors",
|
|
useColors: false,
|
|
text: "Bold Text",
|
|
wantContains: "**Bold Text**",
|
|
},
|
|
{
|
|
name: "with colors",
|
|
useColors: true,
|
|
text: "Bold Text",
|
|
wantContains: "Bold Text",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
formatter := &MarkdownFormatter{UseColors: tt.useColors}
|
|
result := formatter.formatBold(tt.text)
|
|
|
|
if !strings.Contains(result, tt.wantContains) {
|
|
t.Errorf("formatBold() = %q, want to contain %q", result, tt.wantContains)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMarkdownFormatter_Colorize(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
useColors bool
|
|
text string
|
|
color string
|
|
wantColor bool
|
|
}{
|
|
{
|
|
name: "without colors",
|
|
useColors: false,
|
|
text: "Test",
|
|
color: colorRed,
|
|
wantColor: false,
|
|
},
|
|
{
|
|
name: "with colors",
|
|
useColors: true,
|
|
text: "Test",
|
|
color: colorRed,
|
|
wantColor: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
formatter := &MarkdownFormatter{UseColors: tt.useColors}
|
|
result := formatter.colorize(tt.text, tt.color)
|
|
|
|
hasColor := strings.Contains(result, tt.color)
|
|
if hasColor != tt.wantColor {
|
|
t.Errorf("colorize() has color codes = %v, want %v", hasColor, tt.wantColor)
|
|
}
|
|
|
|
if !strings.Contains(result, tt.text) {
|
|
t.Errorf("colorize() doesn't contain original text %q", tt.text)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMarkdownFormatter_FormatContext(t *testing.T) {
|
|
formatter := &MarkdownFormatter{UseColors: false}
|
|
|
|
context := map[string]interface{}{
|
|
"schema": "public",
|
|
"table": "users",
|
|
"column": "id",
|
|
"pattern": "^id_",
|
|
"max_length": 64,
|
|
}
|
|
|
|
result := formatter.formatContext(context)
|
|
|
|
// Should not include schema, table, column (they're in location)
|
|
if strings.Contains(result, "schema") {
|
|
t.Error("formatContext() should skip schema field")
|
|
}
|
|
|
|
if strings.Contains(result, "table=") {
|
|
t.Error("formatContext() should skip table field")
|
|
}
|
|
|
|
if strings.Contains(result, "column=") {
|
|
t.Error("formatContext() should skip column field")
|
|
}
|
|
|
|
// Should include other fields
|
|
if !strings.Contains(result, "pattern") {
|
|
t.Error("formatContext() should include pattern field")
|
|
}
|
|
|
|
if !strings.Contains(result, "max_length") {
|
|
t.Error("formatContext() should include max_length field")
|
|
}
|
|
}
|
|
|
|
func TestMarkdownFormatter_FormatViolation(t *testing.T) {
|
|
formatter := &MarkdownFormatter{UseColors: false}
|
|
|
|
violation := ValidationResult{
|
|
RuleName: "test_rule",
|
|
Level: "error",
|
|
Message: "Test violation message",
|
|
Location: "public.users.id",
|
|
Passed: false,
|
|
Context: map[string]interface{}{
|
|
"pattern": "^id_",
|
|
},
|
|
}
|
|
|
|
result := formatter.formatViolation(violation, colorRed)
|
|
|
|
if !strings.Contains(result, "test_rule") {
|
|
t.Error("formatViolation() should include rule name")
|
|
}
|
|
|
|
if !strings.Contains(result, "Test violation message") {
|
|
t.Error("formatViolation() should include message")
|
|
}
|
|
|
|
if !strings.Contains(result, "public.users.id") {
|
|
t.Error("formatViolation() should include location")
|
|
}
|
|
|
|
if !strings.Contains(result, "Location:") {
|
|
t.Error("formatViolation() should include Location label")
|
|
}
|
|
|
|
if !strings.Contains(result, "Message:") {
|
|
t.Error("formatViolation() should include Message label")
|
|
}
|
|
}
|
|
|
|
func TestReportFormatConstants(t *testing.T) {
|
|
// Test that color constants are defined
|
|
if colorReset == "" {
|
|
t.Error("colorReset is not defined")
|
|
}
|
|
|
|
if colorRed == "" {
|
|
t.Error("colorRed is not defined")
|
|
}
|
|
|
|
if colorYellow == "" {
|
|
t.Error("colorYellow is not defined")
|
|
}
|
|
|
|
if colorGreen == "" {
|
|
t.Error("colorGreen is not defined")
|
|
}
|
|
|
|
if colorBold == "" {
|
|
t.Error("colorBold is not defined")
|
|
}
|
|
}
|