Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions internal/exercises/Catalog/Concepts/03_string_functions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
slug: 03_string_functions
title: String Functions
test_regex: ".*"
hints:
- Use strings.ToLower() for lowercase
- Use strings.ToUpper() for uppercase
- Use strings.ReplaceAll() to replace substrings
- Use strings.Contains(), HasPrefix(), HasSuffix() for checks

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files matching exercise yaml and related Go files:"
fd -a '03_string_functions.yaml|string|catalog|concepts' . 2>/dev/null | sed 's#^\./##' | head -200

echo
echo "Target YAML content:"
if [ -f internal/exercises/Catalog/Concepts/03_string_functions.yaml ]; then
  cat -n internal/exercises/Catalog/Concepts/03_string_functions.yaml
fi

echo
echo "Find HasPrefix/HasSuffix definitions/usages in repository:"
rg -n --hidden --glob '!*.git/*' 'func\s+Has(Prefix|Suffix)|strings\.(Contains|HasPrefix|HasSuffix)|Has(Prefix|Suffix)' . 2>/dev/null | head -300

echo
echo "Check if strings alias is imported in target exercise implementation files:"
rg -n --hidden --glob '!*.git/*' 'import \(\s*(strings)|"strings"|strings\.|Has(Prefix|Suffix)\s*\(' internal/exercises/Catalog/Concepts . 2>/dev/null | head -300

echo
echo "List files under target directory:"
find internal/exercises/Catalog/Concepts -maxdepth 2 -type f 2>/dev/null | sort

Repository: zhravan/golearn

Length of output: 18351


Qualify HasPrefix and HasSuffix with strings.

The template defines HasPrefix and HasSuffix in the same package and tests them directly, so the unqualified names in the hint can resolve to these exercise functions. Use strings.Contains(), strings.HasPrefix(), and strings.HasSuffix() instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/exercises/Catalog/Concepts/03_string_functions.yaml` at line 8,
Update the hint in the string-functions exercise to qualify all checks with the
strings package: use strings.Contains(), strings.HasPrefix(), and
strings.HasSuffix(), avoiding the package-local HasPrefix and HasSuffix
functions.

- Use strings.Split() and strings.Join() for slicing/joining
51 changes: 51 additions & 0 deletions internal/exercises/templates/03_string_functions/strings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package strings_funcs

import "strings"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file location and content =="
if [ -f internal/exercises/templates/03_string_functions/strings.go ]; then
  cat -n internal/exercises/templates/03_string_functions/strings.go
else
  echo "missing file"
fi

echo
echo "== go.mod files =="
fd -a 'go.mod$|go.sum$' . | sed 's#^\./##' | while read -r f; do
  echo "--- ${f#"$PWD/"}"
  sed -n '1,120p' "$f"
done

echo
echo "== lint/config files =="
fd -a 'golangci.*|\.golangci\.ya?ml|\.golangci\.yaml$' . | sed "s#^\./##" | sort | while read -r f; do
  echo "--- ${f#"$PWD/"}"
  sed -n '1,220p' "$f"
done

echo
echo "== package declarations and imports in templates =="
rg -n '^(package |import |import \()' internal/exercises/templates -g '*.go' | head -200

echo
echo "== typecheck/lint config references for templates =="
rg -n 'exercises/templates|templates|golangci|typecheck|unused|enable-all|enable:' . -g '*.json' -g '*.yaml' -g '*.yml' -g '*.toml' -g '*.md' -g '*.go' | head -300

Repository: zhravan/golearn

Length of output: 22132


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== docs verify/incomplete behavior mentions =="
cat -n docs/contributing.md | sed -n '50,75p'
echo "---"
cat -n docs/getting-started.md | sed -n '50,110p'
echo "---"
cat -n CONTRIBUTING.md | sed -n '45,65p'

echo
echo "== CLI verify implementation candidates =="
rg -n 'func .*verify|Verify|verify|template|go run|mod|build|lint|typecheck' internal -g '*.go' | head -250

echo
echo "== inspect relevant CLI file sections =="
ast-grep outline internal/cli/commands.go --match 'func .*Verify' --view expanded || true
sed -n '340,470p' internal/cli/commands.go

Repository: zhravan/golearn

Length of output: 14173


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== internal/cli/commands.go relevant implementation =="
sed -n '120,225p' internal/cli/commands.go

echo
echo "== internal/exercises/solutions.go relevant implementation =="
sed -n '20,85p' internal/exercises/solutions.go

echo
echo "== Go workspace/package references =="
fd -a 'go.work$|go.work.sum$' . | sed 's#^\./##' | while read -r f; do
  echo "--- ${f#"$PWD/"}"
  cat "$f"
done

Repository: zhravan/golearn

Length of output: 5601


Fix the placeholder so it type checks without strings.

golearn verify runs go test ./exercises/03_string_functions, not the embedded internal/exercises/templates package. That package must still compile, but strings_funcs imports "strings" while the placeholder bodies do not use it. If this stub is intended to compile, remove the unused import; do not leave an intentionally broken template unless verification explicitly relies on it.

🧰 Tools
🪛 golangci-lint (2.12.2)

[error] 3-3: : # github.com/zhravan/golearn/internal/exercises/templates/03_string_functions [github.com/zhravan/golearn/internal/exercises/templates/03_string_functions.test]
internal/exercises/templates/03_string_functions/strings.go:3:8: "strings" imported and not used

(typecheck)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/exercises/templates/03_string_functions/strings.go` at line 3,
Remove the unused "strings" import from the strings_funcs placeholder in the
template so the package compiles while preserving the existing stub bodies.

Source: Linters/SAST tools


// Lowercase returns the lowercased version of s.
func Lowercase(s string) string {
// Replace this with the correct implementation
return ""
}

// Uppercase returns the uppercased version of s.
func Uppercase(s string) string {
// Replace this with the correct implementation
return ""
}

// ReplaceAll replaces all occurrences of old with new in s.
func ReplaceAll(s, old, new string) string {
// Replace this with the correct implementation
return ""
}

// Contains checks if s contains substr.
func Contains(s, substr string) bool {
// Replace this with the correct implementation
return false
}

// HasPrefix checks if s starts with prefix.
func HasPrefix(s, prefix string) bool {
// Replace this with the correct implementation
return false
}

// HasSuffix checks if s ends with suffix.
func HasSuffix(s, suffix string) bool {
// Replace this with the correct implementation
return false
}

// Split splits s by sep.
func Split(s, sep string) []string {
// Replace this with the correct implementation
return nil
}

// Join joins elements of slices using sep.
func Join(slices []string, sep string) string {
// Replace this with the correct implementation
return ""
}
64 changes: 64 additions & 0 deletions internal/exercises/templates/03_string_functions/strings_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package strings_funcs

import (
"testing"
)

func TestLowercase(t *testing.T) {
if Lowercase("Hello") != "hello" {
t.Errorf("expected 'hello', got '%s'", Lowercase("Hello"))
}
}

func TestUppercase(t *testing.T) {
if Uppercase("Hello") != "HELLO" {
t.Errorf("expected 'HELLO', got '%s'", Uppercase("Hello"))
}
}

func TestReplaceAll(t *testing.T) {
if ReplaceAll("hello world", "o", "a") != "hella warld" {
t.Errorf("expected 'hella warld', got '%s'", ReplaceAll("hello world", "o", "a"))
}
}

func TestContains(t *testing.T) {
if !Contains("hello", "ell") {
t.Error("expected true")
}
if Contains("hello", "xyz") {
t.Error("expected false")
}
}

func TestHasPrefix(t *testing.T) {
if !HasPrefix("hello", "hel") {
t.Error("expected true")
}
if HasPrefix("hello", "world") {
t.Error("expected false")
}
}

func TestHasSuffix(t *testing.T) {
if !HasSuffix("hello", "llo") {
t.Error("expected true")
}
if HasSuffix("hello", "xyz") {
t.Error("expected false")
}
}

func TestSplit(t *testing.T) {
expected := []string{"a", "b", "c"}
result := Split("a,b,c", ",")
if len(result) != len(expected) {
t.Errorf("expected %v, got %v", expected, result)
}
}
Comment on lines +52 to +58

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files:"
git ls-files | rg 'strings_test\.go|strings\.(go|md|txt)' || true

echo
echo "Test file excerpt:"
file=$(git ls-files | rg '(^|/)strings_test\.go$' | head -n 1 || true)
if [ -n "${file:-}" ]; then
  wc -l "$file"
  sed -n '1,110p' "$file" | cat -n
fi

echo
echo "Implementation candidates:"
for f in $(git ls-files | rg '(^|/)strings\..*$' | head -n 5); do
  echo "--- $f"
  wc -l "$f"
  rg -n "func Split|Split\\(" "$f" || true
done

echo
echo "Behavior probe for current assertion shape:"
python3 - <<'PY'
expected = ["a", "b", "c"]
cases = [
    ["a", "b", "c"],
    ["c", "b", "a"],
    ["x", "y", "z"],
    ["a", "b"],
    ["a", "b", "c", "d"],
]
for result in cases:
    print(result, "current_test_passes=", len(result) == len(expected), "deep_equal=", result == expected)
PY

Repository: zhravan/golearn

Length of output: 2716


Assert the split elements, not only the slice length.

A length check lets any three-element slice pass. Use a full-slice comparison so malformed split results fail.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/exercises/templates/03_string_functions/strings_test.go` around
lines 52 - 58, Update TestSplit to compare result against expected
element-by-element using the project’s established full-slice comparison
approach, rather than checking only len(result). Preserve the existing expected
values and Split("a,b,c", ",") input.


func TestJoin(t *testing.T) {
if Join([]string{"a", "b", "c"}, "-") != "a-b-c" {
t.Errorf("expected 'a-b-c', got '%s'", Join([]string{"a", "b", "c"}, "-"))
}
}