package gemini_test import ( "context" "crypto/tls" "fmt" "io" "mime" "net" "strings" "testing" "time" "source.community/ckaznocha/gemini" "source.community/ckaznocha/gemini/geminitest" ) func Test_response_Input(t *testing.T) { t.Parallel() type args struct { prompt string isSensitive bool } tests := []struct { name string want string args args }{ { name: "", args: args{ prompt: "Hello", }, want: "10 Hello\r\n", }, { name: "", args: args{ prompt: "Hello", isSensitive: true, }, want: "11 Hello\r\n", }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() s := geminitest.NewServer(t, gemini.HandlerFunc(func(ctx context.Context, w gemini.ResponseWriter, r *gemini.Request) { w.Input(ctx, tt.args.prompt, tt.args.isSensitive) })) defer s.Close() conn, err := tls.DialWithDialer( &net.Dialer{Timeout: 1 * time.Second}, "tcp", s.Addr, &tls.Config{ InsecureSkipVerify: true, }, ) if err != nil { t.Fatalf("Unable to dial: error = %v", err) } if _, err = fmt.Fprint(conn, "gemini://localhost.com/\r\n"); err != nil { t.Fatalf("Unable to write request: error = %v", err) } got, err := io.ReadAll(conn) if err != nil { t.Fatalf("Unable to read response: error = %v", err) } if string(got) != tt.want { t.Fatalf("*response.Input(%s, %t) = %s, want %s", tt.args.prompt, tt.args.isSensitive, string(got), tt.want) } }) } } func Test_response_Redirect(t *testing.T) { t.Parallel() type args struct { redirectURL string isPermanant bool } tests := []struct { name string want string args args }{ { name: "", args: args{ redirectURL: "gemini://foo.bar", isPermanant: false, }, want: "30 gemini://foo.bar\r\n", }, { name: "", args: args{ redirectURL: "gemini://foo.bar", isPermanant: true, }, want: "31 gemini://foo.bar\r\n", }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() s := geminitest.NewServer(t, gemini.HandlerFunc(func(ctx context.Context, w gemini.ResponseWriter, r *gemini.Request) { w.Redirect(ctx, tt.args.redirectURL, tt.args.isPermanant) })) defer s.Close() conn, err := tls.DialWithDialer( &net.Dialer{Timeout: 1 * time.Second}, "tcp", s.Addr, &tls.Config{ InsecureSkipVerify: true, }, ) if err != nil { t.Fatalf("Unable to dial: error = %v", err) } if _, err = fmt.Fprint(conn, "gemini://localhost.com/\r\n"); err != nil { t.Fatalf("Unable to write request: error = %v", err) } got, err := io.ReadAll(conn) if err != nil { t.Fatalf("Unable to read response: error = %v", err) } if string(got) != tt.want { t.Fatalf("*response.Redirect(%s, %t) = %s, want %s", tt.args.redirectURL, tt.args.isPermanant, string(got), tt.want) } }) } } func Test_response_Success(t *testing.T) { t.Parallel() type args struct { body io.Reader mimeType string } tests := []struct { name string args args want string }{ { name: "With mime type and body", args: args{ mimeType: mime.FormatMediaType("text/gemini", map[string]string{"charset": "utf-8", "lang": "en"}), body: strings.NewReader("Hello, Gemini!"), }, want: "20 text/gemini; charset=utf-8; lang=en\r\nHello, Gemini!", }, { name: "With body only", args: args{ mimeType: "", body: strings.NewReader("Hello, Gemini!"), }, want: "20 \r\nHello, Gemini!", }, { name: "With mime type only", args: args{ mimeType: mime.FormatMediaType("text/gemini", map[string]string{"charset": "utf-8", "lang": "en"}), body: nil, }, want: "20 text/gemini; charset=utf-8; lang=en\r\n", }, { name: "With no mime type or body", args: args{ mimeType: "", body: nil, }, want: "20 \r\n", }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() s := geminitest.NewServer(t, gemini.HandlerFunc(func(ctx context.Context, w gemini.ResponseWriter, r *gemini.Request) { gotWriter := w.Success(ctx, tt.args.mimeType) if tt.args.body != nil { _, err := io.Copy(gotWriter, tt.args.body) if err != nil { t.Fatal(err) } } })) defer s.Close() conn, err := tls.DialWithDialer( &net.Dialer{Timeout: 1 * time.Second}, "tcp", s.Addr, &tls.Config{ InsecureSkipVerify: true, }, ) if err != nil { t.Fatalf("Unable to dial: error = %v", err) } if _, err = fmt.Fprint(conn, "gemini://localhost.com/\r\n"); err != nil { t.Fatalf("Unable to write request: error = %v", err) } got, err := io.ReadAll(conn) if err != nil { t.Fatalf("Unable to read response: error = %v", err) } if string(got) != tt.want { t.Fatalf("*response.Success(%s) = %s, want %s", tt.args.mimeType, string(got), tt.want) } }) } } func Test_response_Failure(t *testing.T) { t.Parallel() type args struct { msg string code gemini.StatusCode } tests := []struct { name string args args want string }{ { name: "", args: args{ code: gemini.StatusTemporaryFailure, msg: "oops", }, want: "40 oops\r\n", }, { name: "", args: args{ code: gemini.StatusPermanentFailure, msg: "oops", }, want: "50 oops\r\n", }, { name: "", args: args{ code: gemini.StatusTemporaryRedirect, msg: "oops", }, want: "50 oops\r\n", }, } for _, tt := range tests { tt := tt t.Run(tt.name, func(t *testing.T) { t.Parallel() s := geminitest.NewServer(t, gemini.HandlerFunc(func(ctx context.Context, w gemini.ResponseWriter, r *gemini.Request) { w.Failure(ctx, tt.args.code, tt.args.msg) })) defer s.Close() conn, err := tls.DialWithDialer( &net.Dialer{Timeout: 1 * time.Second}, "tcp", s.Addr, &tls.Config{ InsecureSkipVerify: true, }, ) if err != nil { t.Fatalf("Unable to dial: error = %v", err) } if _, err = fmt.Fprint(conn, "gemini://localhost.com/\r\n"); err != nil { t.Fatalf("Unable to write request: error = %v", err) } got, err := io.ReadAll(conn) if err != nil { t.Fatalf("Unable to read response: error = %v", err) } if string(got) != tt.want { t.Fatalf("*response.Failure(%d, %s) = %s, want %s", tt.args.code, tt.args.msg, string(got), tt.want) } }) } }