package lang import ( "strings" "testing" ) func TestGenerateGoHello(t *testing.T) { src := ` package demo fun greet(name: String): String { return "hello, " + name } fun main() { val message = greet("world") println(message) } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `package demo`, `func greet(name string) string`, `return "hello, " + name`, `fmt.Println(message)`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoImports(t *testing.T) { src := ` package demo import strings import rand math.rand fun main() { val upper = strings.ToUpper("go") println(upper) println(rand.Intn(3)) } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `"strings"`, `rand "math/rand"`, `upper := strings.ToUpper("go")`, `fmt.Println(rand.Intn(3))`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoQualifiedTypes(t *testing.T) { src := ` package demo.web import fmt import net.http fun helloHandler(w: http.ResponseWriter, r: *http.Request) { fmt.Fprintln(w, "hello") } fun main() { http.HandleFunc("/", helloHandler) } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `package web`, `func helloHandler(w http.ResponseWriter, r *http.Request)`, `fmt.Fprintln(w, "hello")`, `http.HandleFunc("/", helloHandler)`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoTypedLambda(t *testing.T) { src := ` package demo import fmt import net.http fun main() { http.HandleFunc("/", { w: http.ResponseWriter, r: *http.Request -> fmt.Fprintln(w, "hello from lambda") }) } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {`, `fmt.Fprintln(w, "hello from lambda")`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoTrailingLambda(t *testing.T) { src := ` package demo fun apply(value: String, fn: (String) -> Unit) { fn(value) } fun main() { apply("world") { value -> println(value) } } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `apply("world", func(value string) {`, `fmt.Println(value)`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoHandleFuncKotlinSyntax(t *testing.T) { src := ` package demo import fmt import net.http fun helloHandler(w: http.ResponseWriter, r: *http.Request) { fmt.Fprintln(w, "hello") } fun main() { http.HandleFunc("/") { w, r -> helloHandler(w, r) } } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {`, `helloHandler(w, r)`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoImplicitItLambda(t *testing.T) { src := ` package demo fun apply(value: String, fn: (String) -> Unit) { fn(value) } fun main() { apply("world", { println(it) }) } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `func apply(value string, fn func(string))`, `apply("world", func(it string) {`, `fmt.Println(it)`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoClass(t *testing.T) { src := ` package demo class Greeter(val name: String) { fun greet() { println("hello, " + name) println(this.name) } } fun main() { val greeter = Greeter("world") greeter.greet() } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `type Greeter struct {`, `name string`, `func NewGreeter(name string) *Greeter`, `func (self *Greeter) greet() {`, `fmt.Println("hello, " + self.name)`, `fmt.Println(self.name)`, `greeter := NewGreeter("world")`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoInterface(t *testing.T) { src := ` package demo interface Greeter { fun greet(name: String): String } class ConsoleGreeter(val prefix: String) { fun greet(name: String): String { return prefix + name } } fun greetAll(greeter: Greeter) { println(greeter.greet("world")) } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `type Greeter interface {`, `greet(name string) string`, `type ConsoleGreeter struct {`, `func (self *ConsoleGreeter) greet(name string) string {`, `return self.prefix + name`, `func greetAll(greeter Greeter)`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoHTTPServerWithInterfaceMethodHandler(t *testing.T) { src := ` package examples.http import fmt import net.http interface EpicController { fun hello(w: http.ResponseWriter, r: *http.Request) } class EpicControllerImpl: EpicController { override fun hello(w: http.ResponseWriter, r: *http.Request) { fmt.Fprintln(w, "hello from gotlin") } } fun main() { val epicController: EpicController = EpicControllerImpl() fmt.Println("serving http://localhost:8080") http.HandleFunc("/", epicController.hello) http.ListenAndServe(":8080", http.DefaultServeMux) } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `type EpicController interface {`, `hello(w http.ResponseWriter, r *http.Request)`, `type EpicControllerImpl struct {`, `func NewEpicControllerImpl() *EpicControllerImpl`, `func (self *EpicControllerImpl) hello(w http.ResponseWriter, r *http.Request) {`, `var epicController EpicController = NewEpicControllerImpl()`, `http.HandleFunc("/", epicController.hello)`, `http.ListenAndServe(":8080", http.DefaultServeMux)`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoAliasedStringImport(t *testing.T) { src := ` package demo import _ github.com.lib.pq import database.sql fun main() { println(sql.Drivers()) } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `_ "github.com/lib/pq"`, `"database/sql"`, `fmt.Println(sql.Drivers())`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoMultiAssignAndNull(t *testing.T) { src := ` package demo import database.sql fun main() { val db, err = sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable") if (err != null) { return } db.Close() } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `db, err := sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable")`, `if err != nil {`, `db.Close()`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoAutoBlankImportForSideEffects(t *testing.T) { src := ` package demo import github.com.lib.pq import database.sql fun main() { val db, err = sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable") if (err != null) { return } db.Close() } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `_ "github.com/lib/pq"`, `"database/sql"`, `db, err := sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable")`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoExceptionsAndRunCatching(t *testing.T) { src := ` package demo fun mayFail() { throw "boom" } fun main() { val result = runCatching({ mayFail() }) if (result.isSuccess()) { println("ok") } else { println(result.exceptionOrNull()) } try { mayFail() } catch (e: String) { println(e) } } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `type gotlinResult struct {`, `func gotlinRunCatching(fn func()) (result gotlinResult) {`, `panic("boom")`, `result := gotlinRunCatching(func() {`, `if recovered := recover(); recovered != nil {`, `e := recovered`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoExplicitErrorsForGoErrorResults(t *testing.T) { src := ` package demo import database.sql fun main() { val db = sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable") val err = db.Ping() if (err != null) { throw err } } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `db := sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable")`, `err := db.Ping()`, `if err != nil {`, `panic(err)`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoMigrationInteropHelpers(t *testing.T) { prog, err := Parse(`package demo data class Versioned(var version: Long) fun names(values: Map): List { return keys(values) } fun buffer(): ByteSlice { return ByteSlice(32) } fun optionalError(): Error? { return null }`) if err != nil { t.Fatal(err) } out, err := GenerateGo(prog) if err != nil { t.Fatal(err) } for _, want := range []string{ "Version int64", "for key := range values", "return make([]byte, 32)", "func optionalError() error", } { if !strings.Contains(string(out), want) { t.Fatalf("generated Go missing %q:\n%s", want, out) } } } func TestGenerateGoExplicitErrorsForErrorExprStmt(t *testing.T) { src := ` package demo import database.sql fun main() { val db = sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable") db.Ping() } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `db := sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable")`, `db.Ping()`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoClassWithoutBodyBraces(t *testing.T) { src := ` package demo class User(val Name: String) fun main() { val user = User("alice") println(user.Name) } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `type User struct {`, `func NewUser(Name string) *User`, `user := NewUser("alice")`, `fmt.Println(user.Name)`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoGenericListAndMapTypes(t *testing.T) { src := ` package demo class Store(val names: List, val counts: Map) fun apply(values: List, f: (Map) -> Unit) { f(null) println(values) } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `type Store struct {`, `names []string`, `counts map[string]int`, `func NewStore(names []string, counts map[string]int) *Store`, `func apply(values []string, f func(map[string]int))`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoListAndMapLiterals(t *testing.T) { src := ` package demo fun main() { val names: List = listOf("alice", "bob") val ages: Map = mapOf("alice", 30, "bob", 25) println(names) println(ages) } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `var names []string = []string{"alice", "bob"}`, `var ages map[string]int = map[string]int{"alice": 30, "bob": 25}`, `fmt.Println(names)`, `fmt.Println(ages)`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoListAndMapLiteralsWithCallTypeArgs(t *testing.T) { src := ` package demo fun main() { val test = listOf() val labels = mapOf() println(test) println(labels) } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `test := []int{}`, `labels := map[string]int{}`, `fmt.Println(test)`, `fmt.Println(labels)`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoUnusedLocalDoesNotEmitNamedVariable(t *testing.T) { src := ` package demo fun main() { val test = listOf(1, 7) println("ok") } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `_ = []int{1, 7}`, `fmt.Println("ok")`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } if strings.Contains(code, `test :=`) || strings.Contains(code, `var test `) { t.Fatalf("generated Go should not contain named unused local:\n%s", code) } } func TestGenerateGoGoStatement(t *testing.T) { src := ` package demo import fmt fun runWorker(name: String) { fmt.Println(name) } fun main() { runBlocking { launch { runWorker("alice") } } } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `func runWorker(name string)`, `fmt.Println(name)`, `gotlinScope.Launch`, `runWorker("alice")`, `gotlinRunBlocking`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoForbidsWorkerSelfCalls(t *testing.T) { src := ` package demo worker Counter { val n = 0 fun inc() { inc() } } ` _, err := Parse(src) if err == nil { t.Fatal("expected removed worker syntax error") } } func TestGenerateGoRejectsValReassignment(t *testing.T) { src := ` package demo fun main() { val count = 0 count = 1 } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } _, err = GenerateGo(prog) if err == nil { t.Fatal("expected val reassignment error") } if !strings.Contains(err.Error(), "cannot reassign immutable name count") { t.Fatalf("unexpected error: %v", err) } } func TestGenerateGoDefer(t *testing.T) { src := ` package demo import database.sql fun main() { val rows = sql.Open("postgres", "dsn") defer rows.Close() } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } if !strings.Contains(string(out), "defer rows.Close()") { t.Fatalf("generated Go missing defer:\n%s", out) } } func TestGenerateGoDecimalLiteral(t *testing.T) { src := ` package demo fun interest(): Double { return 0.025 } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } for _, want := range []string{"func interest() float64", "return 0.025"} { if !strings.Contains(string(out), want) { t.Fatalf("generated Go missing %q:\n%s", want, out) } } } func TestGenerateGoRejectsMultiValReassignment(t *testing.T) { src := ` package demo import database.sql fun main() { val db, err = sql.Open("postgres", "postgres://localhost/postgres?sslmode=disable") err = nil } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } _, err = GenerateGo(prog) if err == nil { t.Fatal("expected multi-val reassignment error") } if !strings.Contains(err.Error(), "cannot reassign immutable name err") { t.Fatalf("unexpected error: %v", err) } } func TestGenerateGoAllowsVarReassignment(t *testing.T) { src := ` package demo fun main() { var count = 0 count = 1 count += 2 println(count) } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `count := 0`, `count = 1`, `count += 2`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoRejectsReassignmentToImmutableClassField(t *testing.T) { src := ` package demo class Counter(val count: Int, var total: Int) { fun freeze() { count = 1 } } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } _, err = GenerateGo(prog) if err == nil { t.Fatal("expected immutable class field reassignment error") } if !strings.Contains(err.Error(), "cannot reassign immutable name count") { t.Fatalf("unexpected error: %v", err) } } func TestGenerateGoAllowsReassignmentToMutableClassField(t *testing.T) { src := ` package demo class Counter(var count: Int) { fun inc() { count += 1 } } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } if !strings.Contains(string(out), `self.count += 1`) { t.Fatalf("generated Go missing mutable field assignment:\n%s", string(out)) } } func TestGenerateGoWorkerSyncPanicPropagationScaffolding(t *testing.T) { src := ` package demo worker Counter { fun boom() { throw "x" } } ` _, err := Parse(src) if err == nil { t.Fatal("expected removed worker syntax error") } } func TestParseGoStatementRequiresCall(t *testing.T) { src := ` package demo fun main() { go 123 } ` _, err := Parse(src) if err == nil { t.Fatal("expected parse error") } if !strings.Contains(err.Error(), "bare go is removed") { t.Fatalf("unexpected parse error: %v", err) } } func TestGenerateGoChannelSendAndRead(t *testing.T) { src := ` package demo fun writer(ch: Channel) { ch.send(7) } fun main() { val ch = Channel(1) writer(ch) val v = ch.read() println(v) } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `func writer(ch chan int)`, `ch <- 7`, `ch := make(chan int, 1)`, `writer(ch)`, `v := <-ch`, `fmt.Println(v)`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } } func TestGenerateGoNoTimeImportWhenUnused(t *testing.T) { src := ` package demo fun main() { println("ok") } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) if strings.Contains(code, `"time"`) { t.Fatalf("unexpected time import in generated code:\n%s", code) } } func TestGenerateGoExplicitErrorsForBunStyleCalls(t *testing.T) { src := ` package demo import context import github.com.uptrace.bun class Repo(val db: *bun.DB) { fun run() { val ctx = context.Background() db.NewSelect().ColumnExpr("1").Scan(ctx) val total = db.NewSelect().ColumnExpr("1").Count(ctx) println(total) } } ` prog, err := Parse(src) if err != nil { t.Fatalf("parse failed: %v", err) } out, err := GenerateGo(prog) if err != nil { t.Fatalf("go generation failed: %v", err) } code := string(out) for _, want := range []string{ `self.db.NewSelect().ColumnExpr("1").Scan(ctx)`, `total := self.db.NewSelect().ColumnExpr("1").Count(ctx)`, `fmt.Println(total)`, } { if !strings.Contains(code, want) { t.Fatalf("generated Go missing %q:\n%s", want, code) } } }