Friday, October 24, 2025

An Example of Kafka Topic Consume and Produce by AWS Lambda in Go

In this article, I demonstrate how to consume messages from Amazon MSK (Managed Streaming for Apache Kafka) with AWS Lambda and then produce messages using Lambda in Go.

For security best practices, it’s recommended to use IAM authentication with SASL when sending messages to MSK.

Currently, there are only a few Go libraries available for interacting with MSK. Some of them rely on C dependencies, which is not ideal for Go developers, as it requires enabling CGO and managing additional language dependencies.

To avoid these issues, I used the segmentio/kafka-go library, which is a Kafka client implemented entirely in Go.

MSK IAM SASL Implementation Example

func NewProducer() (*Producer, error) {
	kafkaBrokers := os.Getenv("KAFKA_BROKERS")
	if kafkaBrokers == "" {
		return nil, fmt.Errorf("KAFKA_BROKERS environment variable is required")
	}

	// Validate that we have at least one broker
	brokers := strings.Split(kafkaBrokers, ",")
	if len(brokers) == 0 {
		return nil, fmt.Errorf("KAFKA_BROKERS must contain at least one broker")
	}

	// Trim whitespace from broker addresses
	for i, broker := range brokers {
		brokers[i] = strings.TrimSpace(broker)
		if brokers[i] == "" {
			return nil, fmt.Errorf("invalid empty broker address in KAFKA_BROKERS")
		}
	}

	log.Printf("Kafka producer initializing with brokers: %v", brokers)

	awsCfg, err := config.LoadDefaultConfig(context.Background())
	if err != nil {
		return nil, fmt.Errorf("failed to load AWS config: %w", err)
	}

	// Create IAM SASL mechanism with credentials provider (not static credentials)
	iamSaslMechanism := &aws_msk_iam_v2.Mechanism{
		Signer:      signer.NewSigner(),
		Credentials: awsCfg.Credentials,
		Region:      awsCfg.Region,
	}

	// Configure transport with TLS and SASL
	sharedTransport := &kafka.Transport{
		SASL: iamSaslMechanism,
		TLS:  &tls.Config{},
	}

	// Initialize Kafka writer immediately for provisioned Lambda
	kafkaWriter := &kafka.Writer{
		Addr:         kafka.TCP(brokers...),
		RequiredAcks: kafka.RequireOne,
		BatchTimeout: 10 * time.Millisecond, // For Low latency
		BatchSize:    1,                     // Send messages immediately
		Compression:  kafka.Snappy,          // Use Snappy compression
		Transport:    sharedTransport,
	}

	log.Printf("Kafka producer initialized successfully with %d brokers", len(brokers))

	return &Producer{
		kafkaWriter: kafkaWriter,
	}, nil
}
There is a Go Test, you can put a break point and follow the process how it works. you may need a local stack or AWS Mock to test fully

func TestHandlerWithJSONFile(t *testing.T) {

	// Set up test environment (optional)
	os.Setenv("KAFKA_BROKERS", "localhost:9098,localhost:9098")

	// Read the JSON file
	jsonData, err := os.ReadFile("test/kafka-event-id-41350679.json")
	if err != nil {
		t.Fatalf("Failed to read test JSON file: %v", err)
	}

	// Unmarshal into KafkaEvent
	var event events.KafkaEvent
	if err := json.Unmarshal(jsonData, &event); err != nil {
		t.Fatalf("Failed to unmarshal JSON into KafkaEvent: %v", err)
	}

	// Call the handler function
	ctx := context.Background()
	err = handler(ctx, event)

	// Check result
	if err != nil {
		t.Errorf("Handler returned error: %v", err)
	} else {
		t.Log("Handler executed successfully")
	}

	// Log some details about what was processed
	for topic, records := range event.Records {
		t.Logf("Processed topic: %s with %d records", topic, len(records))
		for _, record := range records {
			t.Logf("  - Partition: %d, Offset: %d", record.Partition, record.Offset)
		}
	}
}
For a whole implementation example, you can refer to the pull request or the branch linked below, which show exactly how to set this up.

https://github.com/gogo-boot/cdk-lambda-go/pull/1



A Practical Guide to Deploying Go Lambdas with AWS CDK

I needed to create a Lambda function with low latency and fast computation. To achieve this, I chose Go, as it is easy to learn and consumes fewer resources compared to other languages.

However, I found that there are very few examples available for building Lambda functions in Go, especially when using AWS CDK or SAM for deployment. Even AI tools couldn't provide sufficient guidance, likely because there isn’t much related content available online.

As a result, I decided to build the solution myself.

Below is a simple example, It is deployed using AWS CDK and utilizes an AWS Linux image, which is lightweight. The CDK makes it straightforward to deploy and manage the Lambda function end to end.

import * as cdk from 'aws-cdk-lib';
import {Duration, RemovalPolicy} from "aws-cdk-lib";
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as path from "path";


export interface MyLambdaStackProps extends cdk.StackProps {}

export class MyLambdaStack extends cdk.Stack {

    constructor(scope: Construct, id: string, props: MyLambdaStackProps) {
        super(scope, id, props);

        // Define the ExampleLambda Lambda function
        const goLambda = new lambda.Function(this, "ExampleLambda", {
            runtime: lambda.Runtime.PROVIDED_AL2023, // Use the custom runtime
            handler: "bootstrap", // Go binary name
            architecture: lambda.Architecture.ARM_64, // Use ARM architecture, cheaper and better performance

            currentVersionOptions: {
                removalPolicy: RemovalPolicy.RETAIN
            },

            // lambda.Code.fromAsset will pack artifact from "/asset-output" directory.
            // therefor the "bootstrap" file must be located in this directory.
            code: lambda.Code.fromAsset(path.join(__dirname, '../example-lambda'), {  // Path to Go binary
                bundling: {
                    image: cdk.DockerImage.fromRegistry("golang:1.25"),
                    command: [
                        'bash', '-c',
                        'GOARCH=arm64 GOOS=linux CGO_ENABLED=0 go build -tags lambda.norpc -o /asset-output/bootstrap main.go'
                    ],
                },
            }),
            // environment: {
            //     'DB_HOST': '',
            // },
            // vpc: this.vpc,
            // vpcSubnets: {
            //     subnets: [subnet1, subnet2, subnet3]
            // },
            // securityGroups: [MySecurityGroup],
            // timeout: cdk.Duration.seconds(10) // Limit timeout to 10 seconds
        });
    }
}


you can have full example code https://github.com/gogo-boot/cdk-lambda-go It has better description in README.md.

You may put the Eventsource `goLambda.addEventSource` for triggering the Lambda by event.

also Provision the Lambda and Auto Scale by `goLambda.addAutoScaling({ minCapacity: 1, maxCapacity: 5 });`, so you lambda will be pre provisioned and run without cold start. so it responses very fast.

Thursday, November 4, 2021

AWS Secret Manager sample code with golang aws-sdk-go-v2 SDK

AWS Secret Manager provides sample code. It is very good to use for quick start. 

But, I see often, they provide sample code with old version of SDK. 

In this time, I program with Golang. and the aws-sdk-go is old version sdk. 

As soon as I pushe the code, I know, it will be alerted by source scanning software. 

I had to modify the sample code. as below

 

package aws

// Use this code snippet in your app.
// If you need more information about configurations or implementing the sample code, visit the AWS docs:
// https://docs.aws.amazon.com/sdk-for-go/v1/developer-guide/setting-up.html

import (
	"context"
	"encoding/base64"
	"errors"
	"fmt"
	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/secretsmanager"
	"github.com/aws/smithy-go"
)

func getSecret() {
	secretName := "arn:aws:secretsmanager:eu-central-1:.... put your secretmanager ARN"
	region := "eu-central-1"

	cfg, err := config.LoadDefaultConfig(context.TODO(),
		config.WithRegion(region),
	)
	if err != nil {
		// handle error
	}

	//Create a Secrets Manager client
	svc := secretsmanager.NewFromConfig(cfg)
	input := &secretsmanager.GetSecretValueInput{
		SecretId:     aws.String(secretName),
		VersionStage: aws.String("AWSCURRENT"), // VersionStage defaults to AWSCURRENT if unspecified
	}

	// In this sample we only handle the specific exceptions for the 'GetSecretValue' API.
	// See https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html

	result, err := svc.GetSecretValue(context.TODO(), input)
	if err != nil {

		var apiErr smithy.APIError
		if errors.As(err, &apiErr) {
			code := apiErr.ErrorCode()
			message := apiErr.ErrorMessage()
			// handle error code
			fmt.Println("error code: " + code + " message : " + message)
			return
		} else {
			// Print the error, cast err to awserr.Error to get the Code and
			// Message from an error.
			fmt.Println(err.Error())
		}
		return
	}

	// Decrypts secret using the associated KMS CMK.
	// Depending on whether the secret is a string or binary, one of these fields will be populated.
	var secretString, decodedBinarySecret string
	if result.SecretString != nil {
		secretString = *result.SecretString
	} else {
		decodedBinarySecretBytes := make([]byte, base64.StdEncoding.DecodedLen(len(result.SecretBinary)))
		len, err := base64.StdEncoding.Decode(decodedBinarySecretBytes, result.SecretBinary)
		if err != nil {
			fmt.Println("Base64 Decode Error:", err)
			return
		}
		decodedBinarySecret = string(decodedBinarySecretBytes[:len])
	}

	// Your code goes here.
	fmt.Println(secretString)
	fmt.Println(decodedBinarySecret)
}

Friday, December 21, 2018

maven with NTLM Proxy Server

While I am migrating my project to Java 11, I was facing many issues. I was not sure if the 3rd Party Framesworks work fine. I had a doubt. So I tested several Frameworks. So far, Eclipslink and Jmokit was not running fine on Java11. I have reported to Jmokit. Eclipselink has apparently fixed something in the latest code, but the latest version is not released yet. It is laid in Snapshot repository. I am using Company Nexus Repository which I am not allow to change the configuration for adding Snapshot Repository. To access the Repository, I had to configure Proxy in maven setting.xml. But, I was not able to access the Repository yet. I realized the Proxy Server is required NTML based Authentication. Maven doesn't support NTLM Proxy Authentication. It required another way to solve.

I found out that either use CNTLM or wagon-http-lightweight. CNTLM would be good for various reson. If you use several application behind Proxy, like a VirtualBox, Git, Yum, SVN, ... But, I just need for a maven. so I have chosen wagon-http-lightweight.

It is fairly easy to use this. You can download the file from https://mvnrepository.com/artifact/org.apache.maven.wagon/wagon-http-lightweight/2.2 This is just 15 Kbytes. locate the dowonloaded file under M2_HOME/lib/ext
Now, You need to configure the maven xml files. You can use the password as plaintext in the configuration. But, I recommend to use encrypted password for the security. maven will recognize automatically when the password starts with "{" and will decrypt it before authenticate with Proxy Server. I will create a master password and user password by maven. masterpassword will be used for decrypting userpassword with in maven. userpassword is your NTLM userpassword

USER_HOME/.m2/settings-security.xml
mvn -emp masterpassword

{SPg1nt21S2MHuw0Hy8MJaEF7Gc7dK25UWGDYKHupNCw=}
<settingsSecurity>
  <master>{SPg1nt21S2MHuw0Hy8MJaEF7Gc7dK25UWGDYKHupNCw=}</master>
</settingsSecurity>
 


Set up your proxy in USER_HOME/.m2/setting.xml

mvn -ep userpassword
{7ut6v4FFiJMHtwsmYrsmLMcPoDBGmbz/kgcQ6Vks+/0=}

<proxies>
    <proxy>
   <id>internet-proxy</id>
   <active>true</active>
   <protocol>http</protocol>
   <host>###ProxyHost###</host>
   <username>###Username###</username>
   <password>{7ut6v4FFiJMHtwsmYrsmLMcPoDBGmbz/kgcQ6Vks+/0=}</password>
   <port>###Proxy Port###</port>
   <nonProxyHosts>localhost|127.0.0.1</nonProxyHosts>
    </proxy>
 </proxies>  


PS. Security is good. but it makes develpers crazy. I hope there is a simple way to secure and easy to devlop without configure extra. it takes so much time to configure and get it.

Thursday, August 10, 2017

Curl Command example with client Key to https Server

To communicate with https Server by curl command, if a client private key is required, it is not simple like as i thought. my_client.12 is my client key and I need to convert to the other format. Before starting, check your key.
openssl pkcs12 -info -in my_client.12



Check the Server sertificate
openssl s_client -showcerts -connect www.domain.com:443


If you see that all right. you will be able to communicate with the https server which requires client key.

Convert the client key
openssl pkcs12 -in my_client.12 -out client.pem -clcerts -nokeys
openssl pkcs12 -in my_client.12 -out key.pem -nocerts
openssl rsa -in key.pem -out newkey.pem


And now finally, you can communicate with the server.
curl -v -G  -k --key newkey.pem --cert ./client.pem:password https://www.domain.com/path

Download Files from AWS S3 Bucket via SQS message



Our Infrastructure put a log file in S3 bucket. I need to get only this new Log file from the S3 bucket and parse it put into DB. The Log parse Servers are clustered which might be inturruped while downloading and might download again which another cluster server is currently parsing the log file.
There is a solution. When new files are created, the S3 Bucket will send a message to a Simple Queue Service(SQS). The Log Parsing Servers will pull the messages every minute. If a message already pulled, SQS will hide a message in determined period until the delete action. The delete action will be called from parsing server after downloading.
To run the code, you will need a python3 or higher and boto3(Amazon Web Services (AWS) SDK for Python)
yum install epel-release
yum install python34-pip
pip3 install boto3



There is a python3 code sample.
import boto3
import json
import traceback
import os
import logging, sys
import tarfile

#logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)

# Get the service resource
sqs = boto3.resource('sqs', region_name='eu-central-1')
s3_client = boto3.client('s3', region_name='eu-central-1')

# Get the queue
queue = sqs.get_queue_by_name(QueueName='Your SQS Queue Name')

for message in queue.receive_messages(MaxNumberOfMessages=10):
  try:
    if message is not None:
      # Parsing event message from s3 bucket
      s3 = json.loads(message.body)['Records'][0]['s3']
      bucket = s3['bucket']['name']
      key = s3['object']['key']
      
      logging.debug('bucket :'+bucket)
      logging.debug('key :'+key)
      
      # Get filename and directory from key
      filename = os.path.basename(key)
      directory = '/your_prefix_dir/' + os.path.dirname(key)
      
      logging.debug('filename :'+filename)
      logging.debug('directory :'+directory)
      
      # Create Directory if it is not exist
      if not os.path.exists(directory):
          os.makedirs(directory)  
                          
      # Download Log File
      s3_client.download_file(bucket, key, directory + filename)
      logging.debug('Download completed')
      
      # Extract tar.gz File
      tar = tarfile.open(directory + filename, "r:gz")
      tar.extractall(directory)
      tar.close()  
      logging.debug('extract file completed')
         
      # Remove tar.gz File
      os.remove(directory + filename)
      
      # Remove SQS message    
      message.delete()
      
  except ValueError:
    # SQS is dedicated for S3 event message. If there is wrong message from other service, leave message body and remove the message
    logging.error('Message format is not valid. Delete message :' + message.body)
    message.delete()
  except Exception:
    logging.error(traceback.format_exc()) 
  else:
    logging.info('finish')



If you see message from the S3 bucket like below command. It is ready to pull the log file by the python script. Watch out, the SQS hide the message default 20 second. it won't be visiable.
aws sqs receive-message --queue-url https://sqs.eu-central-1.amazonaws.com/your account number/queueName --max-number-of-messages 1 --region eu-central-1

you can excute the python script like below 20 seconds later. flock is for avoiding concurrent excution.
flock -n /home/your Dir/lock/s3-copy.lock -c "/usr/bin/python3 /your_prefix_dir/s3-copy.py"

The log file are downloaded and the tar.gz files are extracted.

Thursday, August 3, 2017

Generate Unique Code for Coupons or Vouchers in java

Please see the Maven Dependency
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <scope>test</scope>
  </dependency>
  <dependency>
   <groupId>commons-lang</groupId>
   <artifactId>commons-lang</artifactId>
  </dependency>



import java.math.BigInteger;

public class Coupon {

    private byte numberOfChar;

    private BigInteger code;

    private BigInteger crc;

    public byte getNumberOfChar() {
        return numberOfChar;
    }

    public void setNumberOfChar(byte numberOfChar) {
        this.numberOfChar = numberOfChar;
    }

    public BigInteger getCode() {
        return code;
    }

    public void setCode(BigInteger code) {
        this.code = code;
    }

    public BigInteger getCrc() {
        return crc;
    }

    public void setCrc(BigInteger crc) {
        this.crc = crc;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Coupon)) {
            return false;
        }

        Coupon c = (Coupon) o;
        if (code.equals(c.code)) {
            return true;
        }
        return false;

    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 89 * hash + (code != null ? code.hashCode() : 0);
        hash = 89 * hash + (crc != null ? crc.hashCode() : 0);
        return hash;
    }

}




public class CouponGenerateError extends Exception {

    public CouponGenerateError(String message) {
        super(message);
    }

    private static final long serialVersionUID = -8232285755941178115L;

}




import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.LinkedHashSet;
import java.util.Set;

public class CouponGenerator {

    private SecureRandom random = new SecureRandom();

    /**
     * Represent of number of CRC chracter. If it is 1. One Character of CRC code will be generated and attached next to
     * coupon code
     */
    static int CRC_SIZE = 1;

    /**
     * CRC Generator should never be changed unless you like to invalidate all previously published coupons. It is a key
     * value to prove if the CRC value is right or not
     */
    static int CRC_GENERATOR = 31;

    /**
     * @param numberOfChar
     *            is define how long is the Voucher code. Voucher code consists of coupon code and CRC code
     * @param numberOfCoupon
     *            is define how many Vouchers must be generated
     * @return
     * @throws CouponGenerateError
     */
    public Set getCouponSet(byte numberOfChar, long numberOfCoupon) throws CouponGenerateError {

        isValidNumberOfChar(numberOfChar);

        Set couponSet = new LinkedHashSet();

        while (numberOfCoupon > couponSet.size()) {
            couponSet.add(generateCoupon(numberOfChar));
        }

        return couponSet;
    }

    /**
     * @param numberOfChar
     *            It must be bigger than 2 and smaller than 12. If the parameter is 2, it will have 32 cases of coupon
     *            code. It is meaning less to generate. 
* If the parameter is bigger than 12. Internal long type variable can not calculate it. It will make a * byte overflow and return garbage value. Because, Long is 8 byte which is 64 bits. Each coupon chracter * takes 5 bit. 13 Chracters will take 65 bits * @throws CouponGenerateError */ private void isValidNumberOfChar(byte numberOfChar) throws CouponGenerateError { if (numberOfChar < 3 || numberOfChar > 12) { throw new CouponGenerateError( "Invalid numberOfChar for Coupon chracters. It must be bigger than 2 and smaller than 12"); } } /** * @param numberOfChar * length of Alphanumeric code value is defined by numberOfChar * @param numberOfChar * must be bigger than 3 or equal and smaller than 12 or equal chracters * @return * @throws CouponGenerateError */ public Coupon getCoupon(byte numberOfChar) throws CouponGenerateError { isValidNumberOfChar(numberOfChar); return generateCoupon(numberOfChar); } private Coupon generateCoupon(byte numberOfChar) throws CouponGenerateError { Coupon coupon = new Coupon(); coupon.setNumberOfChar(numberOfChar); // Create 5 random bits per character. 5 bits represent Base32 Encoding coupon.setCode(new BigInteger((numberOfChar - CRC_SIZE) * 5, random)); coupon.setCrc(CouponUtil.calculateCrc(coupon)); return coupon; } }



import java.math.BigInteger;

import org.apache.commons.lang.StringUtils;

public class CouponUtil {

    /**
     * @param coupon
     *            
     * 7.  Base 32 Encoding with Extended Hex Alphabet
     * 
     *    The following description of base 32 is derived from [7].  This
     *    encoding may be referred to as "base32hex".  This encoding should not
     *    be regarded as the same as the "base32" encoding and should not be
     *    referred to as only "base32".  This encoding is used by, e.g.,
     *    NextSECure3 (NSEC3) [10].
     * 
     *    One property with this alphabet, which the base64 and base32
     *    alphabets lack, is that encoded data maintains its sort order when
     *    the encoded data is compared bit-wise.
     * 
     *    This encoding is identical to the previous one, except for the
     *    alphabet.  The new alphabet is found in Table 4.
     * 
     *                  Table 4: The "Extended Hex" Base 32 Alphabet
     * 
     *          Value Encoding  Value Encoding  Value Encoding  Value Encoding
     *              0 0             9 9            18 I            27 R
     *              1 1            10 A            19 J            28 S
     *              2 2            11 B            20 K            29 T
     *              3 3            12 C            21 L            30 U
     *              4 4            13 D            22 M            31 V
     *              5 5            14 E            23 N
     *              6 6            15 F            24 O         (pad) =
     *              7 7            16 G            25 P
     *              8 8            17 H            26 Q
     * 
     * @return
     */
    public static String getVoucherString(Coupon coupon) {

        byte numberOfChar = coupon.getNumberOfChar();

        String code = coupon.getCode().toString(32);

        if (!isCodeSizeRight(numberOfChar, code)) {
            code = leftPadding0(numberOfChar, code);
        }
        // Debug purpose
        // System.out.println("code : " + code);
        // System.out.println("bit code : " + coupon.getCode().toString(2));
        // System.out.println("CRC : " + coupon.getCrc().toString(32));
        return code + coupon.getCrc().toString(32);
    }

    private static String leftPadding0(byte numberOfChar, String code) {

        return StringUtils.repeat("0", numberOfChar - CouponGenerator.CRC_SIZE).substring(
                code.length() % numberOfChar)
                + code;

    }

    private static boolean isCodeSizeRight(byte numberOfChar, String code) {
        return code.length() % (numberOfChar - CouponGenerator.CRC_SIZE) == 0;
    }

    /**
     * It validate the CRC value from the coupon.
     * 
     * @param coupon
     * @return
     */
    static boolean isValidCrc(Coupon coupon) {

        if (coupon.getCrc().equals(calculateCrc(coupon))) {
            return true;
        } else {
            return false;
        }

    }

    /**
     * It cancluate CRC value and return it. coupon parameter must be set code value to calculate CRC value.
     * 
     * @param coupon
     * @return
     */
    static BigInteger calculateCrc(Coupon coupon) {

        BigInteger code = coupon.getCode();
        String crcValue = String.valueOf(code.longValue() % CouponGenerator.CRC_GENERATOR);
        // Debug purpose
        // System.out.println("code.toString(2) : " + code.toString(2));
        // System.out.println("code.longValue() : " + code.longValue());
        // System.out.println("crcValue : " + crcValue);
        return new BigInteger(crcValue, 10);
    }
}




import java.math.BigInteger;
import java.util.Set;

import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;

public class CouponGeneratorTest {

    static CouponGenerator couponGenerator = new CouponGenerator();

    static Set couponSet;

    @BeforeClass
    public static void init() throws CouponGenerateError {

        couponSet = couponGenerator.getCouponSet((byte) 10, 1000);
    }

    /**
     * Test if the CouponGenerator generate requested number of coupons
     * 
     * @throws Exception
     */
    @Test
    public void testCouponCount() throws Exception {

        Assert.assertEquals("Coupon creation didn't match the requested number of coupon", 1000, couponSet.size());
    }

    /**
     * Test if the CodeGenerator generate exact size of vaucher code chracter size.
     * 
     * @throws Exception
     */
    @Test
    public void testCouponCharacterSize() throws Exception {

        for (Coupon coupon : couponSet) {

            // System.out.println(CouponUtil.getVoucherString(coupon));
            Assert.assertEquals("Coupon Characters didn't match the requested number of character", 10, CouponUtil
                    .getVoucherString(coupon).length());
        }

    }

    /**
     * Test if the generated CRC value passes validation check. 
* If it fails, one of generation or validation is wrong * * @throws Exception */ @Test public void testCrcCreation() throws Exception { for (Coupon coupon : couponSet) { Assert.assertTrue("Checksum value is not valid", CouponUtil.isValidCrc(coupon)); } } /** * Test if the given coupon passes CRC validation check. * * @throws Exception */ @Test public void testCrcValidation() throws Exception { Coupon coupon = new Coupon(); coupon.setCode(new BigInteger("54321", 32)); // CRC value is 1. ex ) 54321 (32 radix) % 31 = 5377089 (10 radix) % 31 = 15 (F) coupon.setCrc(new BigInteger("f", 32)); Assert.assertTrue("CRC value value is not valid", CouponUtil.isValidCrc(coupon)); } /** * Test if the given coupon fails CRC validation check. * * @throws Exception */ @Test public void testCrcValidation2() throws Exception { Coupon coupon = new Coupon(); coupon.setCode(new BigInteger("54321", 32)); // CRC value is 1. ex ) 54321 (32 radix) % 31 = 5377089 (10 radix) % 31 = 15 (F) coupon.setCrc(new BigInteger("2", 32)); Assert.assertFalse("CRC value must not be valid", CouponUtil.isValidCrc(coupon)); } /** * Test if the CouponGenerator accept requesting less than 3 chracters or bigger than 12 chracters. In this case, it * must throw Exception */ @Test public void testCouponMinusCharacterSize() { try { couponGenerator.getCoupon((byte) 2); Assert.fail("small charater size is not allowed"); } catch (CouponGenerateError e) { Assert.assertTrue(true); } try { couponGenerator.getCoupon((byte) -6); Assert.fail("Minus charater size is not allowed"); } catch (CouponGenerateError e) { Assert.assertTrue(true); } try { couponGenerator.getCoupon((byte) 13); Assert.fail("Bigger than 12 charater size is not allowed"); } catch (CouponGenerateError e) { Assert.assertTrue(true); } } }