package loldle import ( "strings" "testing" ) // TestRenderGuess_AlignsLabelColumn locks the monospace column-alignment // invariant: the label column must be padded to the longest label so stacked // guesses on the board line up. func TestRenderGuess_AlignsLabelColumn(t *testing.T) { rows := []AttributeRow{ {Key: "gender", Label: "Gender", Type: attrExact, GuessValue: "Male", Result: ResultCorrect}, {Key: "release_date", Label: "Release year", Type: attrYear, GuessValue: "2010", Result: ResultWrong, Direction: "up"}, } out := renderGuess("Aatrox", rows) // HTML envelope must be a
so Telegram renders the columns. if !strings.HasPrefix(out, "
") || !strings.HasSuffix(out, "
") { t.Fatalf("expected
, got %q", out) } // Longest label is "Release year" (12 chars). All inner rows must pad // their label to that width so the value column starts at the same // offset on every line. body := strings.TrimSuffix(strings.TrimPrefix(out, "
"), "
") for _, line := range strings.Split(body, "\n") { if line == "" { continue } // Format: " " // We assert the label is followed by whitespace (or a space-only run // totalling label width) before the value. Easiest check: the value // column always starts after the same number of bytes from the // start-of-line. if !strings.Contains(line, "Release year") && !strings.Contains(line, "Champion ") && !strings.Contains(line, "Gender ") { t.Errorf("row not padded to longest-label width: %q", line) } } // Year direction arrow appears for wrong rows. if !strings.Contains(out, "⬆️") { t.Errorf("expected up-arrow for wrong year row; got %q", out) } } func TestRenderGuess_EscapesHTMLInValues(t *testing.T) { rows := []AttributeRow{ {Key: "gender", Label: "Gender", Type: attrExact, GuessValue: "Tom & ", Result: ResultWrong}, } out := renderGuess("Aatrox", rows) if !strings.Contains(out, "Tom & <Jerry>") { t.Errorf("html metachars not escaped: %s", out) } } func TestRenderBoard_EmptyShowsHint(t *testing.T) { if got := renderBoard(nil); !strings.Contains(got, "No guesses yet") { t.Errorf("empty board placeholder missing: %q", got) } } func TestRenderBoard_StackedGuessesUseUnifiedWidth(t *testing.T) { // First guess has the longest label "Release year"; second guess uses // the same set so unified width should still be 12. Both blocks must // align — verify by checking that a 'Gender' row in the second block // has the same padding as in the first. rowsA := []AttributeRow{ {Key: "gender", Label: "Gender", Type: attrExact, GuessValue: "Male", Result: ResultCorrect}, {Key: "release_date", Label: "Release year", Type: attrYear, GuessValue: "2013", Result: ResultCorrect}, } rowsB := []AttributeRow{ {Key: "gender", Label: "Gender", Type: attrExact, GuessValue: "Female", Result: ResultWrong}, {Key: "release_date", Label: "Release year", Type: attrYear, GuessValue: "2011", Result: ResultWrong, Direction: "up"}, } board := renderBoard([]boardEntry{ {Champion: "Aatrox", Results: rowsA}, {Champion: "Ahri", Results: rowsB}, }) // Two row-groups, blank-line separated, share a single
 envelope.
	if strings.Count(board, "
") != 1 || strings.Count(board, "
") != 1 { t.Errorf("expected single
envelope wrapping both groups; got %q", board) } if !strings.Contains(board, "\n\n") { t.Errorf("expected blank-line separator between groups; got %q", board) } }