You are a backend engineer at a rapidly growing finance startup. The payment processing service in your company is critical and must provide detailed error information for debugging and logging purposes. Currently, the service returns generic error messages, which makes diagnosing issues difficult. Your task is to improve the error handling mechanism by defining a custom error type in Go. This custom error type should include metadata such as an error code, a detailed message, and a timestamp. Additionally, you will simulate a transaction that may fail due to insufficient funds. If the transaction fails, the custom error should be returned; otherwise, a success message should be logged.
CustomError
with the following fields:
Code
(int): A unique identifier for the error.Message
(string): A detailed description of the error.Timestamp
(time.Time): The time when the error occurred.Error()
method for CustomError
so that it satisfies Go’s error
interface. The method should return a formatted string including all metadata.NewCustomError(code int, message string) *CustomError
that initializes and returns a new CustomError
with the current timestamp.simulateTransaction()
that simulates a payment transaction. Use a global variable simulateFailure
to decide the outcome:
simulateFailure
is true
, simulate a failure due to insufficient funds by returning a custom error (error code 1001 and message "Insufficient funds").simulateFailure
is false
, simulate a successful transaction by returning nil
.main
function, call simulateTransaction()
. If an error is returned, log the error details; if not, log a success message.CustomError
(fields and formatted error string).simulateTransaction()
in both failure and success scenarios.When you run the program, you should see one of the following outputs:
simulateFailure
is true
):[ERROR] Transaction failed: Code: 1001, Message: Insufficient funds, Timestamp: 2025-02-13T15:04:05Z
simulateFailure
is false
):[INFO] Transaction succeeded.
. ├── go.mod ├── main.go └── main_test.go
Your program should have the following structure and implement these functions:
main.go
package main import ( "fmt" "time" ) // simulateFailure controls whether the transaction simulation should fail. var simulateFailure = true // CustomError represents a structured error with metadata. type CustomError struct { // Define necessary fields based on requirements } // Add a new method here that implements the error interface // NewCustomError creates a new CustomError instance. func NewCustomError(code int, message string) *CustomError { // Initialize and return a new CustomError instance return nil } // simulateTransaction simulates a payment transaction. // It returns a custom error if the transaction fails (simulateFailure is true), // or nil if the transaction succeeds. func simulateTransaction() error { if simulateFailure { return NewCustomError(1001, "Insufficient funds") } return nil } func main() { err := simulateTransaction() if err != nil { fmt.Printf("[ERROR] Transaction failed: %s\n", err.Error()) } else { fmt.Println("[INFO] Transaction succeeded.") } }
Copy the following test file to main_test.go
to verify your implementation. All test cases must pass.
main_test.go
package main import ( "strings" "testing" "time" ) func TestNewCustomError(t *testing.T) { err := NewCustomError(1001, "Insufficient funds") if err.Code != 1001 { t.Errorf("Expected code 1001, got %d", err.Code) } if err.Message != "Insufficient funds" { t.Errorf("Expected message 'Insufficient funds', got '%s'", err.Message) } if err.Timestamp.IsZero() { t.Error("Expected non-zero timestamp") } // Check the error string format. errorStr := err.Error() if !strings.Contains(errorStr, "Code: 1001") || !strings.Contains(errorStr, "Message: Insufficient funds") || !strings.Contains(errorStr, "Timestamp:") { t.Error("Error string format is incorrect") } } func TestSimulateTransaction_Failure(t *testing.T) { // Force failure. simulateFailure = true err := simulateTransaction() if err == nil { t.Error("Expected an error but got nil") } else { if _, ok := err.(*CustomError); !ok { t.Error("Expected error to be of type *CustomError") } } } func TestSimulateTransaction_Success(t *testing.T) { // Force success. simulateFailure = false err := simulateTransaction() if err != nil { t.Errorf("Expected no error, but got: %v", err) } }
CustomError
struct is defined with fields for error code, message, and timestamp.error
interface should be implemented correctly.NewCustomError
function initializes and returns a valid CustomError
instance.simulateTransaction()
function uses the global simulateFailure
variable to determine whether to return a custom error or nil.main
function logs the correct output based on the transaction result.main_test.go
must pass.time.Now()
to capture the current timestamp when an error is created.fmt.Sprintf
for clarity.