Repository: line/line-bot-sdk-go
Pull request #302 (2022-02-22 08:59:41)

add managing audience api
add managing audience api to SDK.

Unit test is not finished.

Ref issue: [Support Audience apis](https://github.com/line/line-bot-sdk-go/issues/194)


Patch:
diff --git a/linebot/audience.go b/linebot/audience.go
new file mode 100644
index 0000000..eae69dc
--- /dev/null
+++ b/linebot/audience.go
@@ -0,0 +1,880 @@
+package linebot
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/url"
+	"strconv"
+	"strings"
+)
+
+// AudienceStatusType type
+type AudienceStatusType string
+
+// String method
+func (a AudienceStatusType) String() string {
+	return string(a)
+}
+
+const (
+	// INPROGRESS const
+	INPROGRESS AudienceStatusType = "IN_PROGRESS"
+	// READY const
+	READY AudienceStatusType = "READY"
+	// FAILED const
+	FAILED AudienceStatusType = "FAILED"
+	// EXPIRED const
+	EXPIRED AudienceStatusType = "EXPIRED"
+	// INACTIVE const
+	INACTIVE AudienceStatusType = "INACTIVE"
+	// ACTIVATING const
+	ACTIVATING AudienceStatusType = "ACTIVATING"
+)
+
+// AudienceAuthorityLevelType type
+type AudienceAuthorityLevelType string
+
+// String method
+func (a AudienceAuthorityLevelType) String() string {
+	return string(a)
+}
+
+const (
+	// PUBLIC const
+	PUBLIC AudienceAuthorityLevelType = "PUBLIC"
+	// PRIVATE const
+	PRIVATE AudienceAuthorityLevelType = "PRIVATE"
+)
+
+// IUploadAudienceGroupOption type
+type IUploadAudienceGroupOption interface {
+	Apply(call *UploadAudienceGroupCall)
+}
+
+// WithUploadAudienceGroupCallIsIfaAudience func
+func WithUploadAudienceGroupCallIsIfaAudience(isIfaAudience bool) IUploadAudienceGroupOption {
+	return &withUploadAudienceGroupCallIsIfaAudience{
+		isIfaAudience: isIfaAudience,
+	}
+}
+
+type withUploadAudienceGroupCallIsIfaAudience struct {
+	isIfaAudience bool
+}
+
+func (w *withUploadAudienceGroupCallIsIfaAudience) Apply(call *UploadAudienceGroupCall) {
+	call.IsIfaAudience = w.isIfaAudience
+}
+
+// WithUploadAudienceGroupCallUploadDescription func
+func WithUploadAudienceGroupCallUploadDescription(uploadDescription string) IUploadAudienceGroupOption {
+	return &withUploadAudienceGroupCallUploadDescription{
+		uploadDescription: uploadDescription,
+	}
+}
+
+type withUploadAudienceGroupCallUploadDescription struct {
+	uploadDescription string
+}
+
+func (w *withUploadAudienceGroupCallUploadDescription) Apply(call *UploadAudienceGroupCall) {
+	call.UploadDescription = w.uploadDescription
+}
+
+// WithUploadAudienceGroupCallAudiences func
+func WithUploadAudienceGroupCallAudiences(audiences ...string) IUploadAudienceGroupOption {
+	return &withUploadAudienceGroupCallAudiences{
+		audiences: audiences,
+	}
+}
+
+type withUploadAudienceGroupCallAudiences struct {
+	audiences []string
+}
+
+func (w *withUploadAudienceGroupCallAudiences) Apply(call *UploadAudienceGroupCall) {
+	for _, item := range w.audiences {
+		call.Audiences = append(call.Audiences, audience{ID: item})
+	}
+}
+
+// UploadAudienceGroup method
+func (client *Client) UploadAudienceGroup(description string, options ...IUploadAudienceGroupOption) *UploadAudienceGroupCall {
+	call := &UploadAudienceGroupCall{
+		c:           client,
+		Description: description,
+	}
+	for _, item := range options {
+		item.Apply(call)
+	}
+	return call
+}
+
+type audience struct {
+	ID string `json:"id,omitempty"`
+}
+
+// UploadAudienceGroupCall type
+type UploadAudienceGroupCall struct {
+	c                 *Client
+	ctx               context.Context
+	Description       string `validate:"required,max=120"`
+	IsIfaAudience     bool
+	UploadDescription string
+	Audiences         []audience `validate:"max=10000"`
+}
+
+// WithContext method
+func (call *UploadAudienceGroupCall) WithContext(ctx context.Context) *UploadAudienceGroupCall {
+	call.ctx = ctx
+	return call
+}
+
+func (call *UploadAudienceGroupCall) encodeJSON(w io.Writer) error {
+	enc := json.NewEncoder(w)
+	return enc.Encode(struct {
+		Description       string     `json:"description,omitempty"`
+		IsIfaAudience     bool       `json:"isIfaAudience,omitempty"`
+		UploadDescription string     `json:"uploadDescription,omitempty"`
+		Audiences         []audience `json:"audiences,omitempty"`
+	}{
+		Description:       call.Description,
+		IsIfaAudience:     call.IsIfaAudience,
+		UploadDescription: call.UploadDescription,
+		Audiences:         call.Audiences,
+	})
+}
+
+// Do method
+func (call *UploadAudienceGroupCall) Do() (*UploadAudienceGroupResponse, error) {
+	var buf bytes.Buffer
+	if err := call.encodeJSON(&buf); err != nil {
+		return nil, err
+	}
+	res, err := call.c.post(call.ctx, APIAudienceGroupUpload, &buf)
+	if err != nil {
+		return nil, err
+	}
+	defer closeResponse(res)
+	return decodeToAudienceGroupResponse(res)
+}
+
+// IUploadAudienceGroupByFileOption type
+type IUploadAudienceGroupByFileOption interface {
+	Apply(call *UploadAudienceGroupByFileCall)
+}
+
+// WithUploadAudienceGroupByFileCallIsIfaAudience func
+func WithUploadAudienceGroupByFileCallIsIfaAudience(isIfaAudience bool) IUploadAudienceGroupByFileOption {
+	return &withUploadAudienceGroupByFileCallIsIfaAudience{
+		isIfaAudience: isIfaAudience,
+	}
+}
+
+type withUploadAudienceGroupByFileCallIsIfaAudience struct {
+	isIfaAudience bool
+}
+
+func (w *withUploadAudienceGroupByFileCallIsIfaAudience) Apply(call *UploadAudienceGroupByFileCall) {
+	call.IsIfaAudience = w.isIfaAudience
+}
+
+// WithUploadAudienceGroupByFileCallUploadDescription func
+func WithUploadAudienceGroupByFileCallUploadDescription(uploadDescription string) IUploadAudienceGroupByFileOption {
+	return &withUploadAudienceGroupByFileCallUploadDescription{
+		uploadDescription: uploadDescription,
+	}
+}
+
+type withUploadAudienceGroupByFileCallUploadDescription struct {
+	uploadDescription string
+}
+
+func (w *withUploadAudienceGroupByFileCallUploadDescription) Apply(call *UploadAudienceGroupByFileCall) {
+	call.UploadDescription = w.uploadDescription
+}
+
+// UploadAudienceGroupByFile method
+func (client *Client) UploadAudienceGroupByFile(description string, audiences []string, options ...IUploadAudienceGroupByFileOption) *UploadAudienceGroupByFileCall {
+	call := &UploadAudienceGroupByFileCall{
+		c:           client,
+		Description: description,
+		Audiences:   audiences,
+	}
+	for _, item := range options {
+		item.Apply(call)
+	}
+	return call
+}
+
+// UploadAudienceGroupByFileCall type
+type UploadAudienceGroupByFileCall struct {
+	c                 *Client
+	ctx               context.Context
+	Description       string   `json:"description,omitempty" validate:"required,max=120"`
+	IsIfaAudience     bool     `json:"isIfaAudience,omitempty"`
+	UploadDescription string   `json:"uploadDescription,omitempty"`
+	Audiences         []string `json:"audiences,omitempty" validate:"max=1500000"`
+}
+
+// WithContext method
+func (call *UploadAudienceGroupByFileCall) WithContext(ctx context.Context) *UploadAudienceGroupByFileCall {
+	call.ctx = ctx
+	return call
+}
+
+// Do method
+func (call *UploadAudienceGroupByFileCall) Do() (*UploadAudienceGroupResponse, error) {
+	buf := bytes.NewBuffer([]byte{})
+	defer buf.Reset()
+	_, errWriteString := buf.WriteString(strings.Join(call.Audiences, "\n"))
+	if errWriteString != nil {
+		return nil, errWriteString
+	}
+
+	form := map[string]io.Reader{
+		"description": strings.NewReader(call.Description),
+		"file":        buf,
+	}
+	if call.IsIfaAudience {
+		form["isIfaAudience"] = strings.NewReader(strconv.FormatBool(call.IsIfaAudience))
+	}
+	if call.UploadDescription != "" {
+		form["uploadDescription"] = strings.NewReader(call.UploadDescription)
+	}
+	res, err := call.c.postFormFile(call.ctx, APIAudienceGroupUploadByFile, form)
+	if err != nil {
+		return nil, err
+	}
+	defer closeResponse(res)
+	return decodeToAudienceGroupResponse(res)
+}
+
+// IAddAudiencesOption type
+type IAddAudiencesOption interface {
+	Apply(call *AddAudiencesCall)
+}
+
+// WithAddAudiencesCallUploadDescription type
+func WithAddAudiencesCallUploadDescription(uploadDescription string) IAddAudiencesOption {
+	return &withAddAudiencesCallUploadDescription{
+		uploadDescription: uploadDescription,
+	}
+}
+
+type withAddAudiencesCallUploadDescription struct {
+	uploadDescription string
+}
+
+func (w *withAddAudiencesCallUploadDescription) Apply(call *AddAudiencesCall) {
+	call.UploadDescription = w.uploadDescription
+}
+
+// AddAudiences method
+func (client *Client) AddAudiences(audienceGroupID int, audiences []string, options ...IAddAudiencesOption) *AddAudiencesCall {
+	call := &AddAudiencesCall{
+		c:               client,
+		AudienceGroupID: audienceGroupID,
+	}
+	for _, item := range audiences {
+		call.Audiences = append(call.Audiences, audience{ID: item})
+	}
+	for _, item := range options {
+		item.Apply(call)
+	}
+	return call
+}
+
+// AddAudiencesCall type
+type AddAudiencesCall struct {
+	c                 *Client
+	ctx               context.Context
+	AudienceGroupID   int `validate:"required"`
+	UploadDescription string
+	Audiences         []audience `validate:"required,max=10000"`
+}
+
+// WithContext method
+func (call *AddAudiencesCall) WithContext(ctx context.Context) *AddAudiencesCall {
+	call.ctx = ctx
+	return call
+}
+
+func (call *AddAudiencesCall) encodeJSON(w io.Writer) error {
+	enc := json.NewEncoder(w)
+	return enc.Encode(struct {
+		AudienceGroupID   int        `json:"audienceGroupId,omitempty"`
+		UploadDescription string     `json:"uploadDescription,omitempty"`
+		Audiences         []audience `json:"audiences,omitempty"`
+	}{
+		AudienceGroupID:   call.AudienceGroupID,
+		UploadDescription: call.UploadDescription,
+		Audiences:         call.Audiences,
+	})
+}
+
+// Do method
+func (call *AddAudiencesCall) Do() (*BasicResponse, error) {
+	var buf bytes.Buffer
+	if err := call.encodeJSON(&buf); err != nil {
+		return nil, err
+	}
+	res, err := call.c.put(call.ctx, APIAudienceGroupUpload, &buf)
+	if err != nil {
+		return nil, err
+	}
+	defer closeResponse(res)
+	return decodeToBasicResponse(res)
+}
+
+// IAddAudiencesByFileOption type
+type IAddAudiencesByFileOption interface {
+	Apply(call *AddAudiencesByFileCall)
+}
+
+// WithAddAudiencesByFileCallUploadDescription func
+func WithAddAudiencesByFileCallUploadDescription(uploadDescription string) IAddAudiencesByFileOption {
+	return &withAddAudiencesByFileCallUploadDescription{
+		uploadDescription: uploadDescription,
+	}
+}
+
+type withAddAudiencesByFileCallUploadDescription struct {
+	uploadDescription string
+}
+
+func (w *withAddAudiencesByFileCallUploadDescription) Apply(call *AddAudiencesByFileCall) {
+	call.UploadDescription = w.uploadDescription
+}
+
+// AddAudiencesByFile method
+func (client *Client) AddAudiencesByFile(audienceGroupID int, audiences []string, options ...IAddAudiencesByFileOption) *AddAudiencesByFileCall {
+	call := &AddAudiencesByFileCall{
+		c:               client,
+		AudienceGroupID: audienceGroupID,
+		Audiences:       audiences,
+	}
+	for _, item := range options {
+		item.Apply(call)
+	}
+	return call
+}
+
+// AddAudiencesByFileCall type
+type AddAudiencesByFileCall struct {
+	c                 *Client
+	ctx               context.Context
+	AudienceGroupID   int      `json:"audienceGroupId,omitempty" validate:"required"`
+	UploadDescription string   `json:"uploadDescription,omitempty"`
+	Audiences         []string `json:"audiences,omitempty" validate:"required,max=1500000"`
+}
+
+// WithContext method
+func (call *AddAudiencesByFileCall) WithContext(ctx context.Context) *AddAudiencesByFileCall {
+	call.ctx = ctx
+	return call
+}
+
+// Do method
+func (call *AddAudiencesByFileCall) Do() (*BasicResponse, error) {
+	buf := bytes.NewBuffer([]byte{})
+	defer buf.Reset()
+	_, errWriteString := buf.WriteString(strings.Join(call.Audiences, "\n"))
+	if errWriteString != nil {
+		return nil, errWriteString
+	}
+
+	form := map[string]io.Reader{
+		"audienceGroupId": strings.NewReader(strconv.FormatInt(int64(call.AudienceGroupID), 10)),
+		"file":            buf,
+	}
+	if call.UploadDescription != "" {
+		form["uploadDescription"] = strings.NewReader(call.UploadDescription)
+	}
+	res, err := call.c.putFormFile(call.ctx, APIAudienceGroupUploadByFile, form)
+	if err != nil {
+		return nil, err
+	}
+	defer closeResponse(res)
+	return decodeToBasicResponse(res)
+}
+
+// IClickAudienceGroupOption type
+type IClickAudienceGroupOption interface {
+	Apply(call *ClickAudienceGroupCall)
+}
+
+// WithClickAudienceGroupCallClickURL func
+func WithClickAudienceGroupCallClickURL(clickURL string) IClickAudienceGroupOption {
+	return &withClickAudienceGroupCallClickURL{
+		clickURL: clickURL,
+	}
+}
+
+type withClickAudienceGroupCallClickURL struct {
+	clickURL string
+}
+
+func (w *withClickAudienceGroupCallClickURL) Apply(call *ClickAudienceGroupCall) {
+	call.ClickURL = w.clickURL
+}
+
+// ClickAudienceGroup method
+func (client *Client) ClickAudienceGroup(description, requestID string, options ...IClickAudienceGroupOption) *ClickAudienceGroupCall {
+	call := &ClickAudienceGroupCall{
+		c:           client,
+		Description: description,
+		RequestID:   requestID,
+	}
+	for _, item := range options {
+		item.Apply(call)
+	}
+	return call
+}
+
+// ClickAudienceGroupCall type
+type ClickAudienceGroupCall struct {
+	c           *Client
+	ctx         context.Context
+	Description string `validate:"required,max=120"`
+	RequestID   string `validate:"required"`
+	ClickURL    string `validate:"max=2000"`
+}
+
+// WithContext method
+func (call *ClickAudienceGroupCall) WithContext(ctx context.Context) *ClickAudienceGroupCall {
+	call.ctx = ctx
+	return call
+}
+
+func (call *ClickAudienceGroupCall) encodeJSON(w io.Writer) error {
+	enc := json.NewEncoder(w)
+	return enc.Encode(struct {
+		Description string `json:"description,omitempty"`
+		RequestID   string `json:"requestId,omitempty"`
+		ClickURL    string `json:"clickUrl,omitempty"`
+	}{
+		Description: call.Description,
+		RequestID:   call.RequestID,
+		ClickURL:    call.ClickURL,
+	})
+}
+
+// Do method
+func (call *ClickAudienceGroupCall) Do() (*ClickAudienceGroupResponse, error) {
+	var buf bytes.Buffer
+	if err := call.encodeJSON(&buf); err != nil {
+		return nil, err
+	}
+	res, err := call.c.post(call.ctx, APIAudienceGroupClick, &buf)
+	if err != nil {
+		return nil, err
+	}
+	defer closeResponse(res)
+	return decodeToClickAudienceGroupResponse(res)
+}
+
+// IMPAudienceGroup method
+func (client *Client) IMPAudienceGroup(description, requestID string) *IMPAudienceGroupCall {
+	call := &IMPAudienceGroupCall{
+		c:           client,
+		Description: description,
+		RequestID:   requestID,
+	}
+	return call
+}
+
+// IMPAudienceGroupCall type
+type IMPAudienceGroupCall struct {
+	c           *Client
+	ctx         context.Context
+	Description string `validate:"required,max=120"`
+	RequestID   string `validate:"required"`
+}
+
+// WithContext method
+func (call *IMPAudienceGroupCall) WithContext(ctx context.Context) *IMPAudienceGroupCall {
+	call.ctx = ctx
+	return call
+}
+
+func (call *IMPAudienceGroupCall) encodeJSON(w io.Writer) error {
+	enc := json.NewEncoder(w)
+	return enc.Encode(struct {
+		Description string `json:"description,omitempty"`
+		RequestID   string `json:"requestId,omitempty"`
+	}{
+		Description: call.Description,
+		RequestID:   call.RequestID,
+	})
+}
+
+// Do method
+func (call *IMPAudienceGroupCall) Do() (*IMPAudienceGroupResponse, error) {
+	var buf bytes.Buffer
+	if err := call.encodeJSON(&buf); err != nil {
+		return nil, err
+	}
+	res, err := call.c.post(call.ctx, APIAudienceGroupIMP, &buf)
+	if err != nil {
+		return nil, err
+	}
+	defer closeResponse(res)
+	return decodeToIMPAudienceGroupResponse(res)
+}
+
+// UpdateAudienceGroupDescription method
+func (client *Client) UpdateAudienceGroupDescription(audienceGroupID int, description string) *UpdateAudienceGroupDescriptionCall {
+	call := &UpdateAudienceGroupDescriptionCall{
+		c:               client,
+		AudienceGroupID: audienceGroupID,
+		Description:     description,
+	}
+	return call
+}
+
+// UpdateAudienceGroupDescriptionCall type
+type UpdateAudienceGroupDescriptionCall struct {
+	c               *Client
+	ctx             context.Context
+	AudienceGroupID int    `json:"-" validate:"required"`
+	Description     string `validate:"required,max=120"`
+}
+
+// WithContext method
+func (call *UpdateAudienceGroupDescriptionCall) WithContext(ctx context.Context) *UpdateAudienceGroupDescriptionCall {
+	call.ctx = ctx
+	return call
+}
+
+func (call *UpdateAudienceGroupDescriptionCall) encodeJSON(w io.Writer) error {
+	enc := json.NewEncoder(w)
+	return enc.Encode(struct {
+		Description string `json:"description,omitempty"`
+	}{
+		Description: call.Description,
+	})
+}
+
+// Do method
+func (call *UpdateAudienceGroupDescriptionCall) Do() (*BasicResponse, error) {
+	var buf bytes.Buffer
+	if err := call.encodeJSON(&buf); err != nil {
+		return nil, err
+	}
+	res, err := call.c.put(call.ctx, fmt.Sprintf(APIAudienceGroupUpdateDescription, call.AudienceGroupID), &buf)
+	if err != nil {
+		return nil, err
+	}
+	defer closeResponse(res)
+	return decodeToBasicResponse(res)
+}
+
+// ActivateAudienceGroup method
+func (client *Client) ActivateAudienceGroup(audienceGroupID int) *ActivateAudienceGroupCall {
+	call := &ActivateAudienceGroupCall{
+		c:               client,
+		AudienceGroupID: audienceGroupID,
+	}
+	return call
+}
+
+// ActivateAudienceGroupCall type
+type ActivateAudienceGroupCall struct {
+	c               *Client
+	ctx             context.Context
+	AudienceGroupID int `json:"-" validate:"required"`
+}
+
+// WithContext method
+func (call *ActivateAudienceGroupCall) WithContext(ctx context.Context) *ActivateAudienceGroupCall {
+	call.ctx = ctx
+	return call
+}
+
+// Do method
+func (call *ActivateAudienceGroupCall) Do() (*BasicResponse, error) {
+	res, err := call.c.put(call.ctx, fmt.Sprintf(APIAudienceGroupActivate, call.AudienceGroupID), nil)
+	if err != nil {
+		return nil, err
+	}
+	defer closeResponse(res)
+	return decodeToBasicResponse(res)
+}
+
+// DeleteAudienceGroup method
+func (client *Client) DeleteAudienceGroup(audienceGroupID int) *DeleteAudienceGroupCall {
+	call := &DeleteAudienceGroupCall{
+		c:               client,
+		AudienceGroupID: audienceGroupID,
+	}
+	return call
+}
+
+// DeleteAudienceGroupCall type
+type DeleteAudienceGroupCall struct {
+	c               *Client
+	ctx             context.Context
+	AudienceGroupID int `json:"-" validate:"required"`
+}
+
+// WithContext method
+func (call *DeleteAudienceGroupCall) WithContext(ctx context.Context) *DeleteAudienceGroupCall {
+	call.ctx = ctx
+	return call
+}
+
+// Do method
+func (call *DeleteAudienceGroupCall) Do() (*BasicResponse, error) {
+	res, err := call.c.delete(call.ctx, fmt.Sprintf(APIAudienceGroup, call.AudienceGroupID))
+	if err != nil {
+		return nil, err
+	}
+	defer closeResponse(res)
+	return decodeToBasicResponse(res)
+}
+
+// GetAudienceGroup method
+func (client *Client) GetAudienceGroup(audienceGroupID int) *GetAudienceGroupCall {
+	call := &GetAudienceGroupCall{
+		c:               client,
+		AudienceGroupID: audienceGroupID,
+	}
+	return call
+}
+
+// GetAudienceGroupCall type
+type GetAudienceGroupCall struct {
+	c               *Client
+	ctx             context.Context
+	AudienceGroupID int `json:"-" validate:"required"`
+}
+
+// WithContext method
+func (call *GetAudienceGroupCall) WithContext(ctx context.Context) *GetAudienceGroupCall {
+	call.ctx = ctx
+	return call
+}
+
+// Do method
+func (call *GetAudienceGroupCall) Do() (*GetAudienceGroupResponse, error) {
+	res, err := call.c.get(call.ctx, call.c.endpointBase, fmt.Sprintf(APIAudienceGroup, call.AudienceGroupID), nil)
+	if err != nil {
+		return nil, err
+	}
+	defer closeResponse(res)
+	return decodeToGetAudienceGroupResponse(res)
+}
+
+// IListAudienceGroupOption type
+type IListAudienceGroupOption interface {
+	Apply(call *ListAudienceGroupCall)
+}
+
+// WithListAudienceGroupCallDescription func
+func WithListAudienceGroupCallDescription(description string) IListAudienceGroupOption {
+	return &withListAudienceGroupCallDescription{
+		description: description,
+	}
+}
+
+type withListAudienceGroupCallDescription struct {
+	description string
+}
+
+func (w *withListAudienceGroupCallDescription) Apply(call *ListAudienceGroupCall) {
+	call.Description = w.description
+}
+
+// WithListAudienceGroupCallStatus func
+func WithListAudienceGroupCallStatus(status AudienceStatusType) IListAudienceGroupOption {
+	return &withListAudienceGroupCallStatus{
+		status: status,
+	}
+}
+
+type withListAudienceGroupCallStatus struct {
+	status AudienceStatusType
+}
+
+func (w *withListAudienceGroupCallStatus) Apply(call *ListAudienceGroupCall) {
+	call.Status = w.status
+}
+
+// WithListAudienceGroupCallSize func
+func WithListAudienceGroupCallSize(size int) IListAudienceGroupOption {
+	return &withListAudienceGroupCallSize{
+		size: size,
+	}
+}
+
+type withListAudienceGroupCallSize struct {
+	size int
+}
+
+func (w *withListAudienceGroupCallSize) Apply(call *ListAudienceGroupCall) {
+	call.Size = w.size
+}
+
+// WithListAudienceGroupCallIncludesExternalPublicGroups func
+func WithListAudienceGroupCallIncludesExternalPublicGroups(includesExternalPublicGroups bool) IListAudienceGroupOption {
+	return &withListAudienceGroupCallIncludesExternalPublicGroups{
+		includesExternalPublicGroups: includesExternalPublicGroups,
+	}
+}
+
+type withListAudienceGroupCallIncludesExternalPublicGroups struct {
+	includesExternalPublicGroups bool
+}
+
+func (w *withListAudienceGroupCallIncludesExternalPublicGroups) Apply(call *ListAudienceGroupCall) {
+	call.IncludesExternalPublicGroups = w.includesExternalPublicGroups
+}
+
+// WithListAudienceGroupCallCreateRoute func
+func WithListAudienceGroupCallCreateRoute(createRoute string) IListAudienceGroupOption {
+	return &withListAudienceGroupCallCreateRoute{
+		createRoute: createRoute,
+	}
+}
+
+type withListAudienceGroupCallCreateRoute struct {
+	createRoute string
+}
+
+func (w *withListAudienceGroupCallCreateRoute) Apply(call *ListAudienceGroupCall) {
+	call.CreateRoute = w.createRoute
+}
+
+// ListAudienceGroup method
+func (client *Client) ListAudienceGroup(page int, options ...IListAudienceGroupOption) *ListAudienceGroupCall {
+	call := &ListAudienceGroupCall{
+		c:                            client,
+		Page:                         page,
+		IncludesExternalPublicGroups: true,
+	}
+	for _, item := range options {
+		item.Apply(call)
+	}
+	return call
+}
+
+// ListAudienceGroupCall type
+type ListAudienceGroupCall struct {
+	c                            *Client
+	ctx                          context.Context
+	Page                         int                `json:"-" validate:"required,min=1"`
+	Description                  string             `json:"-"`
+	Status                       AudienceStatusType `json:"-"`
+	Size                         int                `json:"-" validate:"min=1,max=40"`
+	IncludesExternalPublicGroups bool               `json:"-"`
+	CreateRoute                  string             `json:"-"`
+}
+
+// WithContext method
+func (call *ListAudienceGroupCall) WithContext(ctx context.Context) *ListAudienceGroupCall {
+	call.ctx = ctx
+	return call
+}
+
+// Do method
+func (call *ListAudienceGroupCall) Do() (*ListAudienceGroupResponse, error) {
+	u := url.Values{}
+	u.Set("page", strconv.FormatInt(int64(call.Page), 10))
+	u.Set("size", strconv.FormatInt(int64(call.Size), 10))
+	if call.Description != "" {
+		u.Set("description", call.Description)
+	}
+	if call.Status != "" {
+		u.Set("status", call.Status.String())
+	}
+	if !call.IncludesExternalPublicGroups {
+		u.Set("includesExternalPublicGroups", strconv.FormatBool(call.IncludesExternalPublicGroups))
+	}
+	if call.CreateRoute != "" {
+		u.Set("createRoute", call.CreateRoute)
+	}
+	res, err := call.c.get(call.ctx, call.c.endpointBase, APIAudienceGroupList, u)
+	if err != nil {
+		return nil, err
+	}
+	defer closeResponse(res)
+	return decodeToListAudienceGroupResponse(res)
+}
+
+// GetAudienceGroupAuthorityLevel method
+func (client *Client) GetAudienceGroupAuthorityLevel() *GetAudienceGroupAuthorityLevelCall {
+	return &GetAudienceGroupAuthorityLevelCall{
+		c: client,
+	}
+}
+
+// GetAudienceGroupAuthorityLevelCall type
+type GetAudienceGroupAuthorityLevelCall struct {
+	c   *Client
+	ctx context.Context
+}
+
+// WithContext method
+func (call *GetAudienceGroupAuthorityLevelCall) WithContext(ctx context.Context) *GetAudienceGroupAuthorityLevelCall {
+	call.ctx = ctx
+	return call
+}
+
+// Do method
+func (call *GetAudienceGroupAuthorityLevelCall) Do() (*GetAudienceGroupAuthorityLevelResponse, error) {
+	res, err := call.c.get(call.ctx, call.c.endpointBase, APIAudienceGroupAuthorityLevel, nil)
+	if err != nil {
+		return nil, err
+	}
+	defer closeResponse(res)
+	return decodeToGetAudienceGroupAuthorityLevelResponse(res)
+}
+
+// ChangeAudienceGroupAuthorityLevel method
+func (client *Client) ChangeAudienceGroupAuthorityLevel(authorityLevel AudienceAuthorityLevelType) *ChangeAudienceGroupAuthorityLevelCall {
+	call := &ChangeAudienceGroupAuthorityLevelCall{
+		c:              client,
+		AuthorityLevel: authorityLevel,
+	}
+	return call
+}
+
+// ChangeAudienceGroupAuthorityLevelCall type
+type ChangeAudienceGroupAuthorityLevelCall struct {
+	c              *Client
+	ctx            context.Context
+	AuthorityLevel AudienceAuthorityLevelType `validate:"required"`
+}
+
+// WithContext method
+func (call *ChangeAudienceGroupAuthorityLevelCall) WithContext(ctx context.Context) *ChangeAudienceGroupAuthorityLevelCall {
+	call.ctx = ctx
+	return call
+}
+
+func (call *ChangeAudienceGroupAuthorityLevelCall) encodeJSON(w io.Writer) error {
+	enc := json.NewEncoder(w)
+	return enc.Encode(struct {
+		AuthorityLevel AudienceAuthorityLevelType `json:"authorityLevel,omitempty"`
+	}{
+		AuthorityLevel: call.AuthorityLevel,
+	})
+}
+
+// Do method
+func (call *ChangeAudienceGroupAuthorityLevelCall) Do() (*BasicResponse, error) {
+	var buf bytes.Buffer
+	if err := call.encodeJSON(&buf); err != nil {
+		return nil, err
+	}
+	res, err := call.c.put(call.ctx, APIAudienceGroupAuthorityLevel, &buf)
+	if err != nil {
+		return nil, err
+	}
+	defer closeResponse(res)
+	return decodeToBasicResponse(res)
+}
diff --git a/linebot/client.go b/linebot/client.go
index 9941d7f..9248830 100644
--- a/linebot/client.go
+++ b/linebot/client.go
@@ -15,13 +15,18 @@
 package linebot
 
 import (
+	"bytes"
 	"context"
 	"errors"
+	"fmt"
 	"io"
 	"io/ioutil"
+	"mime/multipart"
 	"net/http"
+	"net/textproto"
 	"net/url"
 	"path"
+	"time"
 )
 
 // APIEndpoint constants
@@ -91,6 +96,16 @@ const (
 	APIEndpointGetWebhookInfo     = "/v2/bot/channel/webhook/endpoint"
 	APIEndpointSetWebhookEndpoint = "/v2/bot/channel/webhook/endpoint"
 	APIEndpointTestWebhook        = "/v2/bot/channel/webhook/test"
+
+	APIAudienceGroupUpload            = "/v2/bot/audienceGroup/upload"
+	APIAudienceGroupUploadByFile      = "/v2/bot/audienceGroup/upload/byFile"
+	APIAudienceGroupClick             = "/v2/bot/audienceGroup/click"
+	APIAudienceGroupIMP               = "/v2/bot/audienceGroup/imp"
+	APIAudienceGroupUpdateDescription = "/v2/bot/audienceGroup/%d/updateDescription"
+	APIAudienceGroupActivate          = "/v2/bot/audienceGroup/%d/activate"
+	APIAudienceGroup                  = "/v2/bot/audienceGroup/%d"
+	APIAudienceGroupList              = "/v2/bot/audienceGroup/list"
+	APIAudienceGroupAuthorityLevel    = "/v2/bot/audienceGroup/authorityLevel"
 )
 
 // Client type
@@ -221,6 +236,19 @@ func (client *Client) postForm(ctx context.Context, endpoint string, body io.Rea
 	return client.do(ctx, req)
 }
 
+func (client *Client) postFormFile(ctx context.Context, endpoint string, values map[string]io.Reader) (*http.Response, error) {
+	b, contentType, err := uploadFile(values)
+	if err != nil {
+		return nil, err
+	}
+	req, err := http.NewRequest(http.MethodPost, client.url(client.endpointBaseData, endpoint), &b)
+	if err != nil {
+		return nil, err
+	}
+	req.Header.Set("Content-Type", contentType)
+	return client.do(ctx, req)
+}
+
 func (client *Client) put(ctx context.Context, endpoint string, body io.Reader) (*http.Response, error) {
 	req, err := http.NewRequest(http.MethodPut, client.url(client.endpointBase, endpoint), body)
 	if err != nil {
@@ -230,6 +258,19 @@ func (client *Client) put(ctx context.Context, endpoint string, body io.Reader)
 	return client.do(ctx, req)
 }
 
+func (client *Client) putFormFile(ctx context.Context, endpoint string, values map[string]io.Reader) (*http.Response, error) {
+	b, contentType, err := uploadFile(values)
+	if err != nil {
+		return nil, err
+	}
+	req, err := http.NewRequest(http.MethodPut, client.url(client.endpointBaseData, endpoint), &b)
+	if err != nil {
+		return nil, err
+	}
+	req.Header.Set("Content-Type", contentType)
+	return client.do(ctx, req)
+}
+
 func (client *Client) delete(ctx context.Context, endpoint string) (*http.Response, error) {
 	req, err := http.NewRequest(http.MethodDelete, client.url(client.endpointBase, endpoint), nil)
 	if err != nil {
@@ -247,3 +288,34 @@ func closeResponse(res *http.Response) error {
 	_, err := io.Copy(ioutil.Discard, res.Body)
 	return err
 }
+
+func uploadFile(values map[string]io.Reader) (bytes.Buffer, string, error) {
+	var (
+		b   bytes.Buffer
+		err error
+	)
+	w := multipart.NewWriter(&b)
+	for key, r := range values {
+		var fw io.Writer
+		if x, ok := r.(io.Closer); ok {
+			defer x.Close()
+		}
+		if _, ok := r.(*bytes.Buffer); ok {
+			h := make(textproto.MIMEHeader)
+			h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="%s"; filename="%s.txt"`, key, time.Now().Format("20060102150405")))
+			h.Set("Content-Type", "text/plain")
+			if fw, err = w.CreatePart(h); err != nil {
+				return b, "", err
+			}
+		} else {
+			if fw, err = w.CreateFormField(key); err != nil {
+				return b, "", err
+			}
+		}
+		if _, err := io.Copy(fw, r); err != nil {
+			return b, "", err
+		}
+	}
+	w.Close()
+	return b, w.FormDataContentType(), nil
+}
diff --git a/linebot/response.go b/linebot/response.go
index 5937d86..efb9f13 100644
--- a/linebot/response.go
+++ b/linebot/response.go
@@ -271,6 +271,116 @@ type WebhookInfoResponse struct {
 	Active   bool   `json:"active"`
 }
 
+// UploadAudienceGroupResponse type
+type UploadAudienceGroupResponse struct {
+	RequestID         string `json:"-"`
+	AcceptedRequestID string `json:"-"`
+	AudienceGroupID   int    `json:"audienceGroupId,omitempty"`
+	CreateRoute       string `json:"createRoute,omitempty"`
+	Type              string `json:"type,omitempty"`
+	Description       string `json:"description,omitempty"`
+	Created           int64  `json:"created,omitempty"`
+	Permission        string `json:"permission,omitempty"`
+	ExpireTimestamp   int64  `json:"expireTimestamp,omitempty"`
+	IsIfaAudience     bool   `json:"isIfaAudience,omitempty"`
+}
+
+// ClickAudienceGroupResponse type
+type ClickAudienceGroupResponse struct {
+	XRequestID        string `json:"-"` // from header X-Line-Request-Id
+	AcceptedRequestID string `json:"-"`
+	AudienceGroupID   int    `json:"audienceGroupId,omitempty"`
+	CreateRoute       string `json:"createRoute,omitempty"`
+	Type              string `json:"type,omitempty"`
+	Description       string `json:"description,omitempty"`
+	Created           int64  `json:"created,omitempty"`
+	Permission        string `json:"permission,omitempty"`
+	ExpireTimestamp   int64  `json:"expireTimestamp,omitempty"`
+	IsIfaAudience     bool   `json:"isIfaAudience,omitempty"`
+	RequestID         string `json:"requestId,omitempty"`
+	ClickURL          string `json:"clickUrl,omitempty"`
+}
+
+// IMPAudienceGroupResponse type
+type IMPAudienceGroupResponse struct {
+	XRequestID        string `json:"-"`
+	AcceptedRequestID string `json:"-"`
+	AudienceGroupID   int    `json:"audienceGroupId,omitempty"`
+	CreateRoute       string `json:"createRoute,omitempty"`
+	Type              string `json:"type,omitempty"`
+	Description       string `json:"description,omitempty"`
+	Created           int64  `json:"created,omitempty"`
+	Permission        string `json:"permission,omitempty"`
+	ExpireTimestamp   int64  `json:"expireTimestamp,omitempty"`
+	IsIfaAudience     bool   `json:"isIfaAudience,omitempty"`
+	RequestID         string `json:"requestId,omitempty"`
+}
+
+// AudienceGroup type
+type AudienceGroup struct {
+	AudienceGroupID      int    `json:"audienceGroupId,omitempty"`
+	CreateRoute          string `json:"createRoute,omitempty"`
+	Type                 string `json:"type,omitempty"`
+	Description          string `json:"description,omitempty"`
+	Status               string `json:"status,omitempty"`
+	AudienceCount        int    `json:"audienceCount,omitempty"`
+	Created              int64  `json:"created,omitempty"`
+	Permission           string `json:"permission,omitempty"`
+	IsIfaAudience        bool   `json:"isIfaAudience,omitempty"`
+	RequestID            string `json:"requestId,omitempty"`
+	ClickURL             string `json:"clickUrl,omitempty"`
+	FailedType           string `json:"failedType,omitempty"`
+	Activated            int64  `json:"activated,omitempty"`
+	InactivatedTimestamp int64  `json:"inactivatedTimestamp,omitempty"`
+	ExpireTimestamp      int64  `json:"expireTimestamp,omitempty"`
+}
+
+// Job type
+type Job struct {
+	AudienceGroupJobID int    `json:"audienceGroupJobId,omitempty"`
+	AudienceGroupID    int    `json:"audienceGroupId,omitempty"`
+	Description        string `json:"description,omitempty"`
+	Type               string `json:"type,omitempty"`
+	Status             string `json:"status,omitempty"`
+	FailedType         string `json:"failedType,omitempty"`
+	AudienceCount      int64  `json:"audienceCount,omitempty"`
+	Created            int64  `json:"created,omitempty"`
+	JobStatus          string `json:"jobStatus,omitempty"`
+}
+
+// AdAccount type
+type AdAccount struct {
+	Name string `json:"name,omitempty"`
+}
+
+// GetAudienceGroupResponse type
+type GetAudienceGroupResponse struct {
+	RequestID         string        `json:"-"`
+	AcceptedRequestID string        `json:"-"`
+	AudienceGroup     AudienceGroup `json:"audienceGroup,omitempty"`
+	Jobs              []Job         `json:"jobs,omitempty"`
+	AdAccount         *AdAccount    `json:"adaccount,omitempty"`
+}
+
+// ListAudienceGroupResponse type
+type ListAudienceGroupResponse struct {
+	RequestID                        string          `json:"-"`
+	AcceptedRequestID                string          `json:"-"`
+	AudienceGroups                   []AudienceGroup `json:"audienceGroups,omitempty"`
+	HasNextPage                      bool            `json:"hasNextPage,omitempty"`
+	TotalCount                       int             `json:"totalCount,omitempty"`
+	ReadWriteAudienceGroupTotalCount int             `json:"readWriteAudienceGroupTotalCount,omitempty"`
+	Page                             int             `json:"page,omitempty"`
+	Size                             int             `json:"size,omitempty"`
+}
+
+// GetAudienceGroupAuthorityLevelResponse type
+type GetAudienceGroupAuthorityLevelResponse struct {
+	RequestID         string                     `json:"-"`
+	AcceptedRequestID string                     `json:"-"`
+	AuthorityLevel    AudienceAuthorityLevelType `json:"authorityLevel,omitempty"`
+}
+
 // isSuccess checks if status code is 2xx: The action was successfully received,
 // understood, and accepted.
 func isSuccess(code int) bool {
@@ -664,3 +774,111 @@ func decodeToTestWebhookResponse(res *http.Response) (*TestWebhookResponse, erro
 	}
 	return &result, nil
 }
+
+func decodeToAudienceGroupResponse(res *http.Response) (*UploadAudienceGroupResponse, error) {
+	if err := checkResponse(res); err != nil {
+		return nil, err
+	}
+	decoder := json.NewDecoder(res.Body)
+	result := UploadAudienceGroupResponse{
+		RequestID:         res.Header.Get("X-Line-Request-Id"),
+		AcceptedRequestID: res.Header.Get("X-Line-Accepted-Request-Id"),
+	}
+	if err := decoder.Decode(&result); err != nil {
+		if err == io.EOF {
+			return &result, nil
+		}
+		return nil, err
+	}
+	return &result, nil
+}
+
+func decodeToClickAudienceGroupResponse(res *http.Response) (*ClickAudienceGroupResponse, error) {
+	if err := checkResponse(res); err != nil {
+		return nil, err
+	}
+	decoder := json.NewDecoder(res.Body)
+	result := ClickAudienceGroupResponse{
+		XRequestID:        res.Header.Get("X-Line-Request-Id"),
+		AcceptedRequestID: res.Header.Get("X-Line-Accepted-Request-Id"),
+	}
+	if err := decoder.Decode(&result); err != nil {
+		if err == io.EOF {
+			return &result, nil
+		}
+		return nil, err
+	}
+	return &result, nil
+}
+
+func decodeToIMPAudienceGroupResponse(res *http.Response) (*IMPAudienceGroupResponse, error) {
+	if err := checkResponse(res); err != nil {
+		return nil, err
+	}
+	decoder := json.NewDecoder(res.Body)
+	result := IMPAudienceGroupResponse{
+		XRequestID:        res.Header.Get("X-Line-Request-Id"),
+		AcceptedRequestID: res.Header.Get("X-Line-Accepted-Request-Id"),
+	}
+	if err := decoder.Decode(&result); err != nil {
+		if err == io.EOF {
+			return &result, nil
+		}
+		return nil, err
+	}
+	return &result, nil
+}
+
+func decodeToGetAudienceGroupResponse(res *http.Response) (*GetAudienceGroupResponse, error) {
+	if err := checkResponse(res); err != nil {
+		return nil, err
+	}
+	decoder := json.NewDecoder(res.Body)
+	result := GetAudienceGroupResponse{
+		RequestID:         res.Header.Get("X-Line-Request-Id"),
+		AcceptedRequestID: res.Header.Get("X-Line-Accepted-Request-Id"),
+	}
+	if err := decoder.Decode(&result); err != nil {
+		if err == io.EOF {
+			return &result, nil
+		}
+		return nil, err
+	}
+	return &result, nil
+}
+
+func decodeToListAudienceGroupResponse(res *http.Response) (*ListAudienceGroupResponse, error) {
+	if err := checkResponse(res); err != nil {
+		return nil, err
+	}
+	decoder := json.NewDecoder(res.Body)
+	result := ListAudienceGroupResponse{
+		RequestID:         res.Header.Get("X-Line-Request-Id"),
+		AcceptedRequestID: res.Header.Get("X-Line-Accepted-Request-Id"),
+	}
+	if err := decoder.Decode(&result); err != nil {
+		if err == io.EOF {
+			return &result, nil
+		}
+		return nil, err
+	}
+	return &result, nil
+}
+
+func decodeToGetAudienceGroupAuthorityLevelResponse(res *http.Response) (*GetAudienceGroupAuthorityLevelResponse, error) {
+	if err := checkResponse(res); err != nil {
+		return nil, err
+	}
+	decoder := json.NewDecoder(res.Body)
+	result := GetAudienceGroupAuthorityLevelResponse{
+		RequestID:         res.Header.Get("X-Line-Request-Id"),
+		AcceptedRequestID: res.Header.Get("X-Line-Accepted-Request-Id"),
+	}
+	if err := decoder.Decode(&result); err != nil {
+		if err == io.EOF {
+			return &result, nil
+		}
+		return nil, err
+	}
+	return &result, nil
+}


Test patch:
diff --git a/linebot/audience_test.go b/linebot/audience_test.go
new file mode 100644
index 0000000..790664e
--- /dev/null
+++ b/linebot/audience_test.go
@@ -0,0 +1,2276 @@
+package linebot
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"fmt"
+	"io"
+	"log"
+	"net/http"
+	"net/http/httptest"
+	"reflect"
+	"strconv"
+	"testing"
+	"time"
+)
+
+// TestUploadAudienceGroup tests UploadAudienceGroup
+func TestUploadAudienceGroup(t *testing.T) {
+	type RequestBody struct {
+		Description       string     `json:"description,omitempty"`
+		IsIfaAudience     bool       `json:"isIfaAudience,omitempty"`
+		UploadDescription string     `json:"uploadDescription,omitempty"`
+		Audiences         []audience `json:"audiences,omitempty"`
+	}
+	type want struct {
+		URLPath     string
+		RequestBody RequestBody
+		Response    *UploadAudienceGroupResponse
+		Error       error
+	}
+	testCases := []struct {
+		Label             string
+		Description       string
+		IsIfaAudience     bool
+		UploadDescription string
+		Audiences         []string
+		RequestID         string
+		AcceptedRequestID string
+		ResponseCode      int
+		Response          []byte
+		Want              want
+	}{
+		{
+			Label:        "Create Audience Fail",
+			Description:  "audienceGroupName_01",
+			RequestID:    "12222",
+			ResponseCode: http.StatusBadRequest,
+			Response:     []byte(``),
+			Want: want{
+				URLPath: APIAudienceGroupUpload,
+				RequestBody: RequestBody{
+					Description: "audienceGroupName_01",
+				},
+				Error: &APIError{
+					Code: http.StatusBadRequest,
+				},
+			},
+		},
+		{
+			Label:        "Create Audience not include userIDs",
+			Description:  "audienceGroupName_01",
+			RequestID:    "12222",
+			ResponseCode: http.StatusOK,
+			Response:     []byte(`{"audienceGroupId":1234567890123,"createRoute":"MESSAGING_API","type":"UPLOAD","description":"audienceGroupName_01","created":1613698278,"permission":"READ_WRITE","expireTimestamp":1629250278}`),
+			Want: want{
+				URLPath: APIAudienceGroupUpload,
+				RequestBody: RequestBody{
+					Description: "audienceGroupName_01",
+				},
+				Response: &UploadAudienceGroupResponse{
+					RequestID:       "12222",
+					AudienceGroupID: 1234567890123,
+					CreateRoute:     "MESSAGING_API",
+					Type:            "UPLOAD",
+					Description:     "audienceGroupName_01",
+					Created:         1613698278,
+					Permission:      "READ_WRITE",
+					ExpireTimestamp: 1629250278,
+					IsIfaAudience:   false,
+				},
+			},
+		},
+		{
+			Label:       "Create Audience By UserID",
+			Description: "audienceGroupName_01",
+			Audiences: []string{
+				"U4af4980627", "U4af4980628", "U4af4980629",
+			},
+			RequestID:    "12222",
+			ResponseCode: http.StatusOK,
+			Response:     []byte(`{"audienceGroupId":1234567890123,"createRoute":"MESSAGING_API","type":"UPLOAD","description":"audienceGroupName_01","created":1613698278,"permission":"READ_WRITE","expireTimestamp":1629250278}`),
+			Want: want{
+				URLPath: APIAudienceGroupUpload,
+				RequestBody: RequestBody{
+					Description: "audienceGroupName_01",
+					Audiences: []audience{
+						{
+							ID: "U4af4980627",
+						},
+						{
+							ID: "U4af4980628",
+						},
+						{
+							ID: "U4af4980629",
+						},
+					},
+				},
+				Response: &UploadAudienceGroupResponse{
+					RequestID:       "12222",
+					AudienceGroupID: 1234567890123,
+					CreateRoute:     "MESSAGING_API",
+					Type:            "UPLOAD",
+					Description:     "audienceGroupName_01",
+					Created:         1613698278,
+					Permission:      "READ_WRITE",
+					ExpireTimestamp: 1629250278,
+					IsIfaAudience:   false,
+				},
+			},
+		},
+		{
+			Label:         "Create Audience Is IFA",
+			Description:   "audienceGroupName_01",
+			IsIfaAudience: true,
+			RequestID:     "12222",
+			ResponseCode:  http.StatusOK,
+			Response:      []byte(`{"audienceGroupId":1234567890123,"createRoute":"MESSAGING_API","type":"UPLOAD","description":"audienceGroupName_01","created":1613698278,"permission":"READ_WRITE","expireTimestamp":1629250278}`),
+			Want: want{
+				URLPath: APIAudienceGroupUpload,
+				RequestBody: RequestBody{
+					Description:   "audienceGroupName_01",
+					IsIfaAudience: true,
+				},
+				Response: &UploadAudienceGroupResponse{
+					RequestID:       "12222",
+					AudienceGroupID: 1234567890123,
+					CreateRoute:     "MESSAGING_API",
+					Type:            "UPLOAD",
+					Description:     "audienceGroupName_01",
+					Created:         1613698278,
+					Permission:      "READ_WRITE",
+					ExpireTimestamp: 1629250278,
+					IsIfaAudience:   false,
+				},
+			},
+		},
+		{
+			Label:             "Create Audience have uploadDescription",
+			Description:       "audienceGroupName_01",
+			UploadDescription: "audienceGroupNameJob_01",
+			RequestID:         "12222",
+			ResponseCode:      http.StatusOK,
+			Response:          []byte(`{"audienceGroupId":1234567890123,"createRoute":"MESSAGING_API","type":"UPLOAD","description":"audienceGroupName_01","created":1613698278,"permission":"READ_WRITE","expireTimestamp":1629250278}`),
+			Want: want{
+				URLPath: APIAudienceGroupUpload,
+				RequestBody: RequestBody{
+					Description:       "audienceGroupName_01",
+					UploadDescription: "audienceGroupNameJob_01",
+				},
+				Response: &UploadAudienceGroupResponse{
+					RequestID:       "12222",
+					AudienceGroupID: 1234567890123,
+					CreateRoute:     "MESSAGING_API",
+					Type:            "UPLOAD",
+					Description:     "audienceGroupName_01",
+					Created:         1613698278,
+					Permission:      "READ_WRITE",
+					ExpireTimestamp: 1629250278,
+					IsIfaAudience:   false,
+				},
+			},
+		},
+		{
+			Label:             "Create Audience",
+			Description:       "audienceGroupName_01",
+			UploadDescription: "audienceGroupNameJob_01",
+			Audiences: []string{
+				"U4af4980627", "U4af4980628", "U4af4980629",
+			},
+			RequestID:    "12222",
+			ResponseCode: http.StatusOK,
+			Response:     []byte(`{"audienceGroupId":1234567890123,"createRoute":"MESSAGING_API","type":"UPLOAD","description":"audienceGroupName_01","created":1613698278,"permission":"READ_WRITE","expireTimestamp":1629250278}`),
+			Want: want{
+				URLPath: APIAudienceGroupUpload,
+				RequestBody: RequestBody{
+					Description:       "audienceGroupName_01",
+					UploadDescription: "audienceGroupNameJob_01",
+					Audiences: []audience{
+						{
+							ID: "U4af4980627",
+						},
+						{
+							ID: "U4af4980628",
+						},
+						{
+							ID: "U4af4980629",
+						},
+					},
+				},
+				Response: &UploadAudienceGroupResponse{
+					RequestID:       "12222",
+					AudienceGroupID: 1234567890123,
+					CreateRoute:     "MESSAGING_API",
+					Type:            "UPLOAD",
+					Description:     "audienceGroupName_01",
+					Created:         1613698278,
+					Permission:      "READ_WRITE",
+					ExpireTimestamp: 1629250278,
+					IsIfaAudience:   false,
+				},
+			},
+		},
+	}
+
+	var currentTestIdx int
+	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		tc := testCases[currentTestIdx]
+		if r.Method != http.MethodPost {
+			t.Errorf("Method %s; want %s", r.Method, http.MethodPost)
+		}
+		if r.URL.Path != tc.Want.URLPath {
+			t.Errorf("URLPath %s; want %s", r.URL.Path, tc.Want.URLPath)
+		}
+		var result RequestBody
+		if err := json.NewDecoder(r.Body).Decode(&result); err != nil {
+			t.Fatal(err)
+		}
+		if !reflect.DeepEqual(result, tc.Want.RequestBody) {
+			t.Errorf("Request %v; want %v", result, tc.Want.RequestBody)
+		}
+		w.Header().Set("X-Line-Request-Id", tc.RequestID)
+		if tc.AcceptedRequestID != "" {
+			w.Header().Set("X-Line-Accepted-Request-Id", tc.AcceptedRequestID)
+		}
+		w.WriteHeader(tc.ResponseCode)
+		w.Write(tc.Response)
+	}))
+	defer server.Close()
+
+	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		t.Error("Unexpected data API call")
+		w.WriteHeader(http.StatusNotFound)
+		w.Write([]byte(`{"message":"Not found"}`))
+	}))
+	defer dataServer.Close()
+
+	client, err := mockClient(server, dataServer)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var res *UploadAudienceGroupResponse
+	for i, tc := range testCases {
+		currentTestIdx = i
+		t.Run(strconv.Itoa(i)+"/"+tc.Label, func(t *testing.T) {
+			var options []IUploadAudienceGroupOption
+			if len(tc.Audiences) > 0 {
+				options = append(options, WithUploadAudienceGroupCallAudiences(tc.Audiences...))
+			}
+			if tc.IsIfaAudience {
+				options = append(options, WithUploadAudienceGroupCallIsIfaAudience(true))
+			}
+			if tc.UploadDescription != "" {
+				options = append(options, WithUploadAudienceGroupCallUploadDescription(tc.UploadDescription))
+			}
+			timeoutCtx, cancelFn := context.WithTimeout(context.Background(), 1*time.Second)
+			defer cancelFn()
+			res, err = client.UploadAudienceGroup(tc.Description, options...).WithContext(timeoutCtx).Do()
+			if tc.Want.Error != nil {
+				if !reflect.DeepEqual(err, tc.Want.Error) {
+					t.Errorf("Error %v; want %v", err, tc.Want.Error)
+				}
+			} else {
+				if err != nil {
+					t.Error(err)
+				}
+			}
+			if tc.Want.Response != nil {
+				if !reflect.DeepEqual(res, tc.Want.Response) {
+					t.Errorf("Response %v; want %v", res, tc.Want.Response)
+				}
+			}
+		})
+	}
+}
+
+// TestUploadAudienceGroupByFile tests UploadAudienceGroupByFile
+func TestUploadAudienceGroupByFile(t *testing.T) {
+	type RequestBody struct {
+		Description       string
+		IsIfaAudience     bool
+		UploadDescription string
+		Audiences         string
+	}
+	type want struct {
+		URLPath     string
+		RequestBody RequestBody
+		Response    *UploadAudienceGroupResponse
+		Error       error
+	}
+	testCases := []struct {
+		Label             string
+		Description       string
+		IsIfaAudience     bool
+		UploadDescription string
+		Audiences         []string
+		RequestID         string
+		AcceptedRequestID string
+		ResponseCode      int
+		Response          []byte
+		Want              want
+	}{
+		{
+			Label:        "Create Audience By File Fail",
+			Description:  "audienceGroupName_01",
+			RequestID:    "12222",
+			ResponseCode: http.StatusBadRequest,
+			Response:     []byte(``),
+			Want: want{
+				URLPath: APIAudienceGroupUploadByFile,
+				RequestBody: RequestBody{
+					Description: "audienceGroupName_01",
+				},
+				Error: &APIError{
+					Code: http.StatusBadRequest,
+				},
+			},
+		},
+		{
+			Label:        "Create Audience By File not include userIDs",
+			Description:  "audienceGroupName_01",
+			RequestID:    "12222",
+			ResponseCode: http.StatusOK,
+			Response:     []byte(`{"audienceGroupId":1234567890123,"createRoute":"MESSAGING_API","type":"UPLOAD","description":"audienceGroupName_01","created":1613698278,"permission":"READ_WRITE","expireTimestamp":1629250278}`),
+			Want: want{
+				URLPath: APIAudienceGroupUploadByFile,
+				RequestBody: RequestBody{
+					Description: "audienceGroupName_01",
+				},
+				Response: &UploadAudienceGroupResponse{
+					RequestID:       "12222",
+					AudienceGroupID: 1234567890123,
+					CreateRoute:     "MESSAGING_API",
+					Type:            "UPLOAD",
+					Description:     "audienceGroupName_01",
+					Created:         1613698278,
+					Permission:      "READ_WRITE",
+					ExpireTimestamp: 1629250278,
+					IsIfaAudience:   false,
+				},
+			},
+		},
+		{
+			Label:       "Create Audience By File By UserID",
+			Description: "audienceGroupName_01",
+			Audiences: []string{
+				"U4af4980627", "U4af4980628", "U4af4980629",
+			},
+			RequestID:    "12222",
+			ResponseCode: http.StatusOK,
+			Response:     []byte(`{"audienceGroupId":1234567890123,"createRoute":"MESSAGING_API","type":"UPLOAD","description":"audienceGroupName_01","created":1613698278,"permission":"READ_WRITE","expireTimestamp":1629250278}`),
+			Want: want{
+				URLPath: APIAudienceGroupUploadByFile,
+				RequestBody: RequestBody{
+					Description: "audienceGroupName_01",
+					Audiences:   "U4af4980627\nU4af4980628\nU4af4980629",
+				},
+				Response: &UploadAudienceGroupResponse{
+					RequestID:       "12222",
+					AudienceGroupID: 1234567890123,
+					CreateRoute:     "MESSAGING_API",
+					Type:            "UPLOAD",
+					Description:     "audienceGroupName_01",
+					Created:         1613698278,
+					Permission:      "READ_WRITE",
+					ExpireTimestamp: 1629250278,
+					IsIfaAudience:   false,
+				},
+			},
+		},
+		{
+			Label:         "Create Audience By File Is IFA",
+			Description:   "audienceGroupName_01",
+			IsIfaAudience: true,
+			RequestID:     "12222",
+			ResponseCode:  http.StatusOK,
+			Response:      []byte(`{"audienceGroupId":1234567890123,"createRoute":"MESSAGING_API","type":"UPLOAD","description":"audienceGroupName_01","created":1613698278,"permission":"READ_WRITE","expireTimestamp":1629250278}`),
+			Want: want{
+				URLPath: APIAudienceGroupUploadByFile,
+				RequestBody: RequestBody{
+					Description:   "audienceGroupName_01",
+					IsIfaAudience: true,
+				},
+				Response: &UploadAudienceGroupResponse{
+					RequestID:       "12222",
+					AudienceGroupID: 1234567890123,
+					CreateRoute:     "MESSAGING_API",
+					Type:            "UPLOAD",
+					Description:     "audienceGroupName_01",
+					Created:         1613698278,
+					Permission:      "READ_WRITE",
+					ExpireTimestamp: 1629250278,
+					IsIfaAudience:   false,
+				},
+			},
+		},
+		{
+			Label:             "Create Audience By File have uploadDescription",
+			Description:       "audienceGroupName_01",
+			UploadDescription: "audienceGroupNameJob_01",
+			RequestID:         "12222",
+			ResponseCode:      http.StatusOK,
+			Response:          []byte(`{"audienceGroupId":1234567890123,"createRoute":"MESSAGING_API","type":"UPLOAD","description":"audienceGroupName_01","created":1613698278,"permission":"READ_WRITE","expireTimestamp":1629250278}`),
+			Want: want{
+				URLPath: APIAudienceGroupUploadByFile,
+				RequestBody: RequestBody{
+					Description:       "audienceGroupName_01",
+					UploadDescription: "audienceGroupNameJob_01",
+				},
+				Response: &UploadAudienceGroupResponse{
+					RequestID:       "12222",
+					AudienceGroupID: 1234567890123,
+					CreateRoute:     "MESSAGING_API",
+					Type:            "UPLOAD",
+					Description:     "audienceGroupName_01",
+					Created:         1613698278,
+					Permission:      "READ_WRITE",
+					ExpireTimestamp: 1629250278,
+					IsIfaAudience:   false,
+				},
+			},
+		},
+		{
+			Label:             "Create Audience By File",
+			Description:       "audienceGroupName_01",
+			UploadDescription: "audienceGroupNameJob_01",
+			Audiences: []string{
+				"U4af4980627", "U4af4980628", "U4af4980629",
+			},
+			RequestID:    "12222",
+			ResponseCode: http.StatusOK,
+			Response:     []byte(`{"audienceGroupId":1234567890123,"createRoute":"MESSAGING_API","type":"UPLOAD","description":"audienceGroupName_01","created":1613698278,"permission":"READ_WRITE","expireTimestamp":1629250278}`),
+			Want: want{
+				URLPath: APIAudienceGroupUploadByFile,
+				RequestBody: RequestBody{
+					Description:       "audienceGroupName_01",
+					UploadDescription: "audienceGroupNameJob_01",
+					Audiences:         "U4af4980627\nU4af4980628\nU4af4980629",
+				},
+				Response: &UploadAudienceGroupResponse{
+					RequestID:       "12222",
+					AudienceGroupID: 1234567890123,
+					CreateRoute:     "MESSAGING_API",
+					Type:            "UPLOAD",
+					Description:     "audienceGroupName_01",
+					Created:         1613698278,
+					Permission:      "READ_WRITE",
+					ExpireTimestamp: 1629250278,
+					IsIfaAudience:   false,
+				},
+			},
+		},
+	}
+
+	var currentTestIdx int
+	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		t.Error("Unexpected API call")
+		w.WriteHeader(http.StatusNotFound)
+		w.Write([]byte(`{"message":"Not found"}`))
+	}))
+	defer server.Close()
+
+	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		tc := testCases[currentTestIdx]
+		if r.Method != http.MethodPost {
+			t.Errorf("Method %s; want %s", r.Method, http.MethodPost)
+		}
+		if r.URL.Path != tc.Want.URLPath {
+			t.Errorf("URLPath %s; want %s", r.URL.Path, tc.Want.URLPath)
+		}
+
+		if err := r.ParseMultipartForm(32 << 20); err != nil {
+			t.Fatal(err)
+		}
+		file, _, err := r.FormFile("file")
+		if err != nil {
+			t.Fatal(err)
+		}
+		defer file.Close()
+		buf := bytes.NewBuffer(nil)
+		_, errCopy := io.Copy(buf, file)
+		if err != nil {
+			t.Fatal(errCopy)
+		}
+		if buf.String() != tc.Want.RequestBody.Audiences {
+			t.Errorf("Audiences %s; want %s", buf.String(), tc.Want.RequestBody.Audiences)
+		}
+		if v := r.FormValue("description"); v != "" {
+			if v != tc.Want.RequestBody.Description {
+				t.Errorf("Description %s; want %s", v, tc.Want.RequestBody.Description)
+			}
+		}
+		if v := r.FormValue("uploadDescription"); v != "" {
+			if v != tc.Want.RequestBody.UploadDescription {
+				t.Errorf("UploadDescription %s; want %s", v, tc.Want.RequestBody.UploadDescription)
+			}
+		}
+		if v := r.FormValue("isIfaAudience"); v != "" {
+			isIfaAudience, err := strconv.ParseBool(v)
+			if err != nil {
+				t.Fatal(errCopy)
+			}
+			if isIfaAudience != tc.Want.RequestBody.IsIfaAudience {
+				t.Errorf("IsIfaAudience %s; want %v", v, tc.Want.RequestBody.IsIfaAudience)
+			}
+		}
+
+		w.Header().Set("X-Line-Request-Id", tc.RequestID)
+		if tc.AcceptedRequestID != "" {
+			w.Header().Set("X-Line-Accepted-Request-Id", tc.AcceptedRequestID)
+		}
+		w.WriteHeader(tc.ResponseCode)
+		w.Write(tc.Response)
+	}))
+	defer dataServer.Close()
+
+	client, err := mockClient(server, dataServer)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var res *UploadAudienceGroupResponse
+	for i, tc := range testCases {
+		currentTestIdx = i
+		t.Run(strconv.Itoa(i)+"/"+tc.Label, func(t *testing.T) {
+			var options []IUploadAudienceGroupByFileOption
+			if tc.IsIfaAudience {
+				options = append(options, WithUploadAudienceGroupByFileCallIsIfaAudience(true))
+			}
+			if tc.UploadDescription != "" {
+				options = append(options, WithUploadAudienceGroupByFileCallUploadDescription(tc.UploadDescription))
+			}
+			timeoutCtx, cancelFn := context.WithTimeout(context.Background(), 1*time.Second)
+			defer cancelFn()
+			res, err = client.UploadAudienceGroupByFile(tc.Description, tc.Audiences, options...).WithContext(timeoutCtx).Do()
+			if tc.Want.Error != nil {
+				if !reflect.DeepEqual(err, tc.Want.Error) {
+					t.Errorf("Error %v; want %v", err, tc.Want.Error)
+				}
+			} else {
+				if err != nil {
+					t.Error(err)
+				}
+			}
+			if tc.Want.Response != nil {
+				if !reflect.DeepEqual(res, tc.Want.Response) {
+					t.Errorf("Response %v; want %v", res, tc.Want.Response)
+				}
+			}
+		})
+	}
+}
+
+// TestAddAudiences tests AddAudiences
+func TestAddAudiences(t *testing.T) {
+	type RequestBody struct {
+		AudienceGroupID   int        `json:"audienceGroupId,omitempty"`
+		UploadDescription string     `json:"uploadDescription,omitempty"`
+		Audiences         []audience `json:"audiences,omitempty"`
+	}
+	type want struct {
+		URLPath     string
+		RequestBody RequestBody
+		Response    *BasicResponse
+		Error       error
+	}
+	testCases := []struct {
+		Label             string
+		AudienceGroupID   int
+		UploadDescription string
+		Audiences         []string
+		RequestID         string
+		AcceptedRequestID string
+		ResponseCode      int
+		Response          []byte
+		Want              want
+	}{
+		{
+			Label:             "Add Audience Fail",
+			AudienceGroupID:   4389303728991,
+			UploadDescription: "audienceGroupNameJob_01",
+			Audiences: []string{
+				"U4af4980627", "U4af4980628", "U4af4980629",
+			},
+			RequestID:    "12222",
+			ResponseCode: http.StatusBadRequest,
+			Response:     []byte(``),
+			Want: want{
+				URLPath: APIAudienceGroupUpload,
+				RequestBody: RequestBody{
+					AudienceGroupID:   4389303728991,
+					UploadDescription: "audienceGroupNameJob_01",
+					Audiences: []audience{
+						{
+							ID: "U4af4980627",
+						},
+						{
+							ID: "U4af4980628",
+						},
+						{
+							ID: "U4af4980629",
+						},
+					},
+				},
+				Error: &APIError{
+					Code: http.StatusBadRequest,
+				},
+			},
+		},
+		{
+			Label:           "add Audience no uploadDescription",
+			AudienceGroupID: 4389303728991,
+			Audiences: []string{
+				"U4af4980627", "U4af4980628", "U4af4980629",
+			},
+			RequestID:    "12222",
+			ResponseCode: http.StatusOK,
+			Response:     []byte(``),
+			Want: want{
+				URLPath: APIAudienceGroupUpload,
+				RequestBody: RequestBody{
+					AudienceGroupID: 4389303728991,
+					Audiences: []audience{
+						{
+							ID: "U4af4980627",
+						},
+						{
+							ID: "U4af4980628",
+						},
+						{
+							ID: "U4af4980629",
+						},
+					},
+				},
+				Response: &BasicResponse{
+					RequestID: "12222",
+				},
+			},
+		},
+		{
+			Label:             "add Audience",
+			AudienceGroupID:   4389303728991,
+			UploadDescription: "audienceGroupNameJob_01",
+			Audiences: []string{
+				"U4af4980627", "U4af4980628", "U4af4980629",
+			},
+			RequestID:    "12222",
+			ResponseCode: http.StatusOK,
+			Response:     []byte(``),
+			Want: want{
+				URLPath: APIAudienceGroupUpload,
+				RequestBody: RequestBody{
+					AudienceGroupID:   4389303728991,
+					UploadDescription: "audienceGroupNameJob_01",
+					Audiences: []audience{
+						{
+							ID: "U4af4980627",
+						},
+						{
+							ID: "U4af4980628",
+						},
+						{
+							ID: "U4af4980629",
+						},
+					},
+				},
+				Response: &BasicResponse{
+					RequestID: "12222",
+				},
+			},
+		},
+	}
+
+	var currentTestIdx int
+	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		tc := testCases[currentTestIdx]
+		if r.Method != http.MethodPut {
+			t.Errorf("Method %s; want %s", r.Method, http.MethodPut)
+		}
+		if r.URL.Path != tc.Want.URLPath {
+			t.Errorf("URLPath %s; want %s", r.URL.Path, tc.Want.URLPath)
+		}
+		var result RequestBody
+		if err := json.NewDecoder(r.Body).Decode(&result); err != nil {
+			t.Fatal(err)
+		}
+		if !reflect.DeepEqual(result, tc.Want.RequestBody) {
+			t.Errorf("Request %v; want %v", result, tc.Want.RequestBody)
+		}
+		w.Header().Set("X-Line-Request-Id", tc.RequestID)
+		if tc.AcceptedRequestID != "" {
+			w.Header().Set("X-Line-Accepted-Request-Id", tc.AcceptedRequestID)
+		}
+		w.WriteHeader(tc.ResponseCode)
+		w.Write(tc.Response)
+	}))
+	defer server.Close()
+
+	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		t.Error("Unexpected data API call")
+		w.WriteHeader(http.StatusNotFound)
+		w.Write([]byte(`{"message":"Not found"}`))
+	}))
+	defer dataServer.Close()
+
+	client, err := mockClient(server, dataServer)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var res *BasicResponse
+	for i, tc := range testCases {
+		currentTestIdx = i
+		t.Run(strconv.Itoa(i)+"/"+tc.Label, func(t *testing.T) {
+			var options []IAddAudiencesOption
+			if tc.UploadDescription != "" {
+				options = append(options, WithAddAudiencesCallUploadDescription(tc.UploadDescription))
+			}
+			timeoutCtx, cancelFn := context.WithTimeout(context.Background(), 1*time.Second)
+			defer cancelFn()
+			res, err = client.AddAudiences(tc.AudienceGroupID, tc.Audiences, options...).WithContext(timeoutCtx).Do()
+			if tc.Want.Error != nil {
+				log.Println(err)
+				log.Println(tc.Want.Error)
+				if !reflect.DeepEqual(err, tc.Want.Error) {
+					t.Errorf("Error %v; want %v", err, tc.Want.Error)
+				}
+			} else {
+				if err != nil {
+					t.Error(err)
+				}
+			}
+			if tc.Want.Response != nil {
+				if !reflect.DeepEqual(res, tc.Want.Response) {
+					t.Errorf("Response %v; want %v", res, tc.Want.Response)
+				}
+			}
+		})
+	}
+}
+
+// TestAddAudiencesByFile tests AddAudiencesByFile
+func TestAddAudiencesByFile(t *testing.T) {
+	type RequestBody struct {
+		AudienceGroupID   int
+		UploadDescription string
+		Audiences         string
+	}
+	type want struct {
+		URLPath     string
+		RequestBody RequestBody
+		Response    *BasicResponse
+		Error       error
+	}
+	testCases := []struct {
+		Label             string
+		AudienceGroupID   int
+		UploadDescription string
+		Audiences         []string
+		RequestID         string
+		AcceptedRequestID string
+		ResponseCode      int
+		Response          []byte
+		Want              want
+	}{
+		{
+			Label:             "Add Audience By File Fail",
+			AudienceGroupID:   4389303728991,
+			UploadDescription: "audienceGroupNameJob_01",
+			Audiences: []string{
+				"U4af4980627", "U4af4980628", "U4af4980629",
+			},
+			RequestID:    "12222",
+			ResponseCode: http.StatusBadRequest,
+			Response:     []byte(``),
+			Want: want{
+				URLPath: APIAudienceGroupUploadByFile,
+				RequestBody: RequestBody{
+					AudienceGroupID:   4389303728991,
+					UploadDescription: "audienceGroupNameJob_01",
+					Audiences:         "U4af4980627\nU4af4980628\nU4af4980629",
+				},
+				Error: &APIError{
+					Code: http.StatusBadRequest,
+				},
+			},
+		},
+		{
+			Label:           "Add Audience By File no uploadDescription",
+			AudienceGroupID: 4389303728991,
+			Audiences: []string{
+				"U4af4980627", "U4af4980628", "U4af4980629",
+			},
+			RequestID:    "12222",
+			ResponseCode: http.StatusOK,
+			Response:     []byte(``),
+			Want: want{
+				URLPath: APIAudienceGroupUploadByFile,
+				RequestBody: RequestBody{
+					AudienceGroupID: 4389303728991,
+					Audiences:       "U4af4980627\nU4af4980628\nU4af4980629",
+				},
+				Response: &BasicResponse{
+					RequestID: "12222",
+				},
+			},
+		},
+		{
+			Label:             "Add Audience By File",
+			AudienceGroupID:   4389303728991,
+			UploadDescription: "audienceGroupNameJob_01",
+			Audiences: []string{
+				"U4af4980627", "U4af4980628", "U4af4980629",
+			},
+			RequestID:    "12222",
+			ResponseCode: http.StatusOK,
+			Response:     []byte(``),
+			Want: want{
+				URLPath: APIAudienceGroupUploadByFile,
+				RequestBody: RequestBody{
+					AudienceGroupID:   4389303728991,
+					UploadDescription: "audienceGroupNameJob_01",
+					Audiences:         "U4af4980627\nU4af4980628\nU4af4980629",
+				},
+				Response: &BasicResponse{
+					RequestID: "12222",
+				},
+			},
+		},
+	}
+
+	var currentTestIdx int
+	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		t.Error("Unexpected API call")
+		w.WriteHeader(http.StatusNotFound)
+		w.Write([]byte(`{"message":"Not found"}`))
+	}))
+	defer server.Close()
+
+	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		tc := testCases[currentTestIdx]
+		if r.Method != http.MethodPut {
+			t.Errorf("Method %s; want %s", r.Method, http.MethodPut)
+		}
+		if r.URL.Path != tc.Want.URLPath {
+			t.Errorf("URLPath %s; want %s", r.URL.Path, tc.Want.URLPath)
+		}
+
+		if err := r.ParseMultipartForm(32 << 20); err != nil {
+			t.Fatal(err)
+		}
+		file, _, err := r.FormFile("file")
+		if err != nil {
+			t.Fatal(err)
+		}
+		defer file.Close()
+		buf := bytes.NewBuffer(nil)
+		_, errCopy := io.Copy(buf, file)
+		if err != nil {
+			t.Fatal(errCopy)
+		}
+		if buf.String() != tc.Want.RequestBody.Audiences {
+			t.Errorf("Audiences %s; want %s", buf.String(), tc.Want.RequestBody.Audiences)
+		}
+		if v := r.FormValue("audienceGroupId"); v != "" {
+			audienceGroupID, err := strconv.ParseInt(v, 10, 64)
+			if err != nil {
+				t.Fatal(err)
+			}
+			if int(audienceGroupID) != tc.Want.RequestBody.AudienceGroupID {
+				t.Errorf("audienceGroupId %v; want %v", int(audienceGroupID), tc.Want.RequestBody.AudienceGroupID)
+			}
+		}
+		if v := r.FormValue("uploadDescription"); v != "" {
+			if v != tc.Want.RequestBody.UploadDescription {
+				t.Errorf("UploadDescription %s; want %s", v, tc.Want.RequestBody.UploadDescription)
+			}
+		}
+
+		w.Header().Set("X-Line-Request-Id", tc.RequestID)
+		if tc.AcceptedRequestID != "" {
+			w.Header().Set("X-Line-Accepted-Request-Id", tc.AcceptedRequestID)
+		}
+		w.WriteHeader(tc.ResponseCode)
+		w.Write(tc.Response)
+	}))
+	defer dataServer.Close()
+
+	client, err := mockClient(server, dataServer)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var res *BasicResponse
+	for i, tc := range testCases {
+		currentTestIdx = i
+		t.Run(strconv.Itoa(i)+"/"+tc.Label, func(t *testing.T) {
+			var options []IAddAudiencesByFileOption
+			if tc.UploadDescription != "" {
+				options = append(options, WithAddAudiencesByFileCallUploadDescription(tc.UploadDescription))
+			}
+			timeoutCtx, cancelFn := context.WithTimeout(context.Background(), 1*time.Second)
+			defer cancelFn()
+			res, err = client.AddAudiencesByFile(tc.AudienceGroupID, tc.Audiences, options...).WithContext(timeoutCtx).Do()
+			if tc.Want.Error != nil {
+				if !reflect.DeepEqual(err, tc.Want.Error) {
+					t.Errorf("Error %v; want %v", err, tc.Want.Error)
+				}
+			} else {
+				if err != nil {
+					t.Error(err)
+				}
+			}
+			if tc.Want.Response != nil {
+				if !reflect.DeepEqual(res, tc.Want.Response) {
+					t.Errorf("Response %v; want %v", res, tc.Want.Response)
+				}
+			}
+		})
+	}
+}
+
+// TestClickAudienceGroup tests ClickAudienceGroup
+func TestClickAudienceGroup(t *testing.T) {
+	type RequestBody struct {
+		Description string `json:"description,omitempty"`
+		RequestID   string `json:"requestId,omitempty"`
+		ClickURL    string `json:"clickUrl,omitempty"`
+	}
+	type want struct {
+		URLPath     string
+		RequestBody RequestBody
+		Response    *ClickAudienceGroupResponse
+		Error       error
+	}
+	testCases := []struct {
+		Label             string
+		Description       string
+		XRequestID        string
+		ClickURL          string
+		RequestID         string
+		AcceptedRequestID string
+		ResponseCode      int
+		Response          []byte
+		Want              want
+	}{
+		{
+			Label:        "Click Audience Fail",
+			Description:  "audienceGroupName_01",
+			XRequestID:   "bb9744f9-47fa-4a29-941e-1234567890ab",
+			ClickURL:     "https://developers.line.biz/",
+			RequestID:    "12222",
+			ResponseCode: http.StatusBadRequest,
+			Response:     []byte(``),
+			Want: want{
+				URLPath: APIAudienceGroupClick,
+				RequestBody: RequestBody{
+					Description: "audienceGroupName_01",
+					RequestID:   "bb9744f9-47fa-4a29-941e-1234567890ab",
+					ClickURL:    "https://developers.line.biz/",
+				},
+				Error: &APIError{
+					Code: http.StatusBadRequest,
+				},
+			},
+		},
+		{
+			Label:        "Click Audience No ClickURL",
+			Description:  "audienceGroupName_01",
+			XRequestID:   "bb9744f9-47fa-4a29-941e-1234567890ab",
+			RequestID:    "12222",
+			ResponseCode: http.StatusOK,
+			Response:     []byte(`{"audienceGroupId":1234567890123,"createRoute":"MESSAGING_API","type":"CLICK","description":"audienceGroupName_01","created":1613705240,"permission":"READ_WRITE","expireTimestamp":1629257239,"requestId":"bb9744f9-47fa-4a29-941e-1234567890ab","clickUrl":"https://developers.line.biz/"}`),
+			Want: want{
+				URLPath: APIAudienceGroupClick,
+				RequestBody: RequestBody{
+					Description: "audienceGroupName_01",
+					RequestID:   "bb9744f9-47fa-4a29-941e-1234567890ab",
+				},
+				Response: &ClickAudienceGroupResponse{
+					XRequestID:      "12222",
+					AudienceGroupID: 1234567890123,
+					CreateRoute:     "MESSAGING_API",
+					Type:            "CLICK",
+					Description:     "audienceGroupName_01",
+					Created:         1613705240,
+					Permission:      "READ_WRITE",
+					ExpireTimestamp: 1629257239,
+					IsIfaAudience:   false,
+					RequestID:       "bb9744f9-47fa-4a29-941e-1234567890ab",
+					ClickURL:        "https://developers.line.biz/",
+				},
+			},
+		},
+		{
+			Label:        "Click Audience",
+			Description:  "audienceGroupName_01",
+			XRequestID:   "bb9744f9-47fa-4a29-941e-1234567890ab",
+			ClickURL:     "https://developers.line.biz/",
+			RequestID:    "12222",
+			ResponseCode: http.StatusOK,
+			Response:     []byte(`{"audienceGroupId":1234567890123,"createRoute":"MESSAGING_API","type":"CLICK","description":"audienceGroupName_01","created":1613705240,"permission":"READ_WRITE","expireTimestamp":1629257239,"requestId":"bb9744f9-47fa-4a29-941e-1234567890ab","clickUrl":"https://developers.line.biz/"}`),
+			Want: want{
+				URLPath: APIAudienceGroupClick,
+				RequestBody: RequestBody{
+					Description: "audienceGroupName_01",
+					RequestID:   "bb9744f9-47fa-4a29-941e-1234567890ab",
+					ClickURL:    "https://developers.line.biz/",
+				},
+				Response: &ClickAudienceGroupResponse{
+					XRequestID:      "12222",
+					AudienceGroupID: 1234567890123,
+					CreateRoute:     "MESSAGING_API",
+					Type:            "CLICK",
+					Description:     "audienceGroupName_01",
+					Created:         1613705240,
+					Permission:      "READ_WRITE",
+					ExpireTimestamp: 1629257239,
+					IsIfaAudience:   false,
+					RequestID:       "bb9744f9-47fa-4a29-941e-1234567890ab",
+					ClickURL:        "https://developers.line.biz/",
+				},
+			},
+		},
+	}
+
+	var currentTestIdx int
+	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		tc := testCases[currentTestIdx]
+		if r.Method != http.MethodPost {
+			t.Errorf("Method %s; want %s", r.Method, http.MethodPost)
+		}
+		if r.URL.Path != tc.Want.URLPath {
+			t.Errorf("URLPath %s; want %s", r.URL.Path, tc.Want.URLPath)
+		}
+		var result RequestBody
+		if err := json.NewDecoder(r.Body).Decode(&result); err != nil {
+			t.Fatal(err)
+		}
+		if !reflect.DeepEqual(result, tc.Want.RequestBody) {
+			t.Errorf("Request %v; want %v", result, tc.Want.RequestBody)
+		}
+		w.Header().Set("X-Line-Request-Id", tc.RequestID)
+		if tc.AcceptedRequestID != "" {
+			w.Header().Set("X-Line-Accepted-Request-Id", tc.AcceptedRequestID)
+		}
+		w.WriteHeader(tc.ResponseCode)
+		w.Write(tc.Response)
+	}))
+	defer server.Close()
+
+	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		t.Error("Unexpected data API call")
+		w.WriteHeader(http.StatusNotFound)
+		w.Write([]byte(`{"message":"Not found"}`))
+	}))
+	defer dataServer.Close()
+
+	client, err := mockClient(server, dataServer)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var res *ClickAudienceGroupResponse
+	for i, tc := range testCases {
+		currentTestIdx = i
+		t.Run(strconv.Itoa(i)+"/"+tc.Label, func(t *testing.T) {
+			var options []IClickAudienceGroupOption
+			if tc.ClickURL != "" {
+				options = append(options, WithClickAudienceGroupCallClickURL(tc.ClickURL))
+			}
+			timeoutCtx, cancelFn := context.WithTimeout(context.Background(), 1*time.Second)
+			defer cancelFn()
+			res, err = client.ClickAudienceGroup(tc.Description, tc.XRequestID, options...).WithContext(timeoutCtx).Do()
+			if tc.Want.Error != nil {
+				log.Println(err)
+				log.Println(tc.Want.Error)
+				if !reflect.DeepEqual(err, tc.Want.Error) {
+					t.Errorf("Error %v; want %v", err, tc.Want.Error)
+				}
+			} else {
+				if err != nil {
+					t.Error(err)
+				}
+			}
+			if tc.Want.Response != nil {
+				if !reflect.DeepEqual(res, tc.Want.Response) {
+					t.Errorf("Response %v; want %v", res, tc.Want.Response)
+				}
+			}
+		})
+	}
+}
+
+// TestIMPAudienceGroup tests IMPAudienceGroup
+func TestIMPAudienceGroup(t *testing.T) {
+	type RequestBody struct {
+		Description string `json:"description,omitempty"`
+		RequestID   string `json:"requestId,omitempty"`
+	}
+	type want struct {
+		URLPath     string
+		RequestBody RequestBody
+		Response    *IMPAudienceGroupResponse
+		Error       error
+	}
+	testCases := []struct {
+		Label             string
+		Description       string
+		XRequestID        string
+		RequestID         string
+		AcceptedRequestID string
+		ResponseCode      int
+		Response          []byte
+		Want              want
+	}{
+		{
+			Label:        "IMP Audience Fail",
+			Description:  "audienceGroupName_01",
+			XRequestID:   "bb9744f9-47fa-4a29-941e-1234567890ab",
+			RequestID:    "12222",
+			ResponseCode: http.StatusBadRequest,
+			Response:     []byte(``),
+			Want: want{
+				URLPath: APIAudienceGroupIMP,
+				RequestBody: RequestBody{
+					Description: "audienceGroupName_01",
+					RequestID:   "bb9744f9-47fa-4a29-941e-1234567890ab",
+				},
+				Error: &APIError{
+					Code: http.StatusBadRequest,
+				},
+			},
+		},
+		{
+			Label:        "IMP Audience",
+			Description:  "audienceGroupName_01",
+			XRequestID:   "bb9744f9-47fa-4a29-941e-1234567890ab",
+			RequestID:    "12222",
+			ResponseCode: http.StatusOK,
+			Response:     []byte(`{"audienceGroupId":1234567890123,"createRoute":"MESSAGING_API","type":"IMP","description":"audienceGroupName_01","created":1613707097,"permission":"READ_WRITE","expireTimestamp":1629259095,"requestId":"bb9744f9-47fa-4a29-941e-1234567890ab"}`),
+			Want: want{
+				URLPath: APIAudienceGroupIMP,
+				RequestBody: RequestBody{
+					Description: "audienceGroupName_01",
+					RequestID:   "bb9744f9-47fa-4a29-941e-1234567890ab",
+				},
+				Response: &IMPAudienceGroupResponse{
+					XRequestID:      "12222",
+					AudienceGroupID: 1234567890123,
+					CreateRoute:     "MESSAGING_API",
+					Type:            "IMP",
+					Description:     "audienceGroupName_01",
+					Created:         1613707097,
+					Permission:      "READ_WRITE",
+					ExpireTimestamp: 1629259095,
+					IsIfaAudience:   false,
+					RequestID:       "bb9744f9-47fa-4a29-941e-1234567890ab",
+				},
+			},
+		},
+	}
+
+	var currentTestIdx int
+	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		tc := testCases[currentTestIdx]
+		if r.Method != http.MethodPost {
+			t.Errorf("Method %s; want %s", r.Method, http.MethodPost)
+		}
+		if r.URL.Path != tc.Want.URLPath {
+			t.Errorf("URLPath %s; want %s", r.URL.Path, tc.Want.URLPath)
+		}
+		var result RequestBody
+		if err := json.NewDecoder(r.Body).Decode(&result); err != nil {
+			t.Fatal(err)
+		}
+		if !reflect.DeepEqual(result, tc.Want.RequestBody) {
+			t.Errorf("Request %v; want %v", result, tc.Want.RequestBody)
+		}
+		w.Header().Set("X-Line-Request-Id", tc.RequestID)
+		if tc.AcceptedRequestID != "" {
+			w.Header().Set("X-Line-Accepted-Request-Id", tc.AcceptedRequestID)
+		}
+		w.WriteHeader(tc.ResponseCode)
+		w.Write(tc.Response)
+	}))
+	defer server.Close()
+
+	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		t.Error("Unexpected data API call")
+		w.WriteHeader(http.StatusNotFound)
+		w.Write([]byte(`{"message":"Not found"}`))
+	}))
+	defer dataServer.Close()
+
+	client, err := mockClient(server, dataServer)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var res *IMPAudienceGroupResponse
+	for i, tc := range testCases {
+		currentTestIdx = i
+		t.Run(strconv.Itoa(i)+"/"+tc.Label, func(t *testing.T) {
+			timeoutCtx, cancelFn := context.WithTimeout(context.Background(), 1*time.Second)
+			defer cancelFn()
+			res, err = client.IMPAudienceGroup(tc.Description, tc.XRequestID).WithContext(timeoutCtx).Do()
+			if tc.Want.Error != nil {
+				log.Println(err)
+				log.Println(tc.Want.Error)
+				if !reflect.DeepEqual(err, tc.Want.Error) {
+					t.Errorf("Error %v; want %v", err, tc.Want.Error)
+				}
+			} else {
+				if err != nil {
+					t.Error(err)
+				}
+			}
+			if tc.Want.Response != nil {
+				if !reflect.DeepEqual(res, tc.Want.Response) {
+					t.Errorf("Response %v; want %v", res, tc.Want.Response)
+				}
+			}
+		})
+	}
+}
+
+// TestUpdateAudienceGroupDescription tests UpdateAudienceGroupDescription
+func TestUpdateAudienceGroupDescription(t *testing.T) {
+	type RequestBody struct {
+		Description string `json:"description,omitempty"`
+	}
+	type want struct {
+		URLPath     string
+		RequestBody RequestBody
+		Response    *BasicResponse
+		Error       error
+	}
+	testCases := []struct {
+		Label             string
+		AudienceGroupID   int
+		Description       string
+		RequestID         string
+		AcceptedRequestID string
+		ResponseCode      int
+		Response          []byte
+		Want              want
+	}{
+		{
+			Label:           "Update Audience Description Fail",
+			AudienceGroupID: 1234567890123,
+			Description:     "audienceGroupName_01",
+			RequestID:       "12222",
+			ResponseCode:    http.StatusBadRequest,
+			Response:        []byte(``),
+			Want: want{
+				URLPath: fmt.Sprintf(APIAudienceGroupUpdateDescription, 1234567890123),
+				RequestBody: RequestBody{
+					Description: "audienceGroupName_01",
+				},
+				Error: &APIError{
+					Code: http.StatusBadRequest,
+				},
+			},
+		},
+		{
+			Label:           "Update Audience Description",
+			AudienceGroupID: 1234567890123,
+			Description:     "audienceGroupName_01",
+			RequestID:       "12222",
+			ResponseCode:    http.StatusOK,
+			Response:        []byte(``),
+			Want: want{
+				URLPath: fmt.Sprintf(APIAudienceGroupUpdateDescription, 1234567890123),
+				RequestBody: RequestBody{
+					Description: "audienceGroupName_01",
+				},
+				Response: &BasicResponse{
+					RequestID: "12222",
+				},
+			},
+		},
+	}
+
+	var currentTestIdx int
+	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		tc := testCases[currentTestIdx]
+		if r.Method != http.MethodPut {
+			t.Errorf("Method %s; want %s", r.Method, http.MethodPut)
+		}
+		if r.URL.Path != tc.Want.URLPath {
+			t.Errorf("URLPath %s; want %s", r.URL.Path, tc.Want.URLPath)
+		}
+		var result RequestBody
+		if err := json.NewDecoder(r.Body).Decode(&result); err != nil {
+			t.Fatal(err)
+		}
+		if !reflect.DeepEqual(result, tc.Want.RequestBody) {
+			t.Errorf("Request %v; want %v", result, tc.Want.RequestBody)
+		}
+		w.Header().Set("X-Line-Request-Id", tc.RequestID)
+		if tc.AcceptedRequestID != "" {
+			w.Header().Set("X-Line-Accepted-Request-Id", tc.AcceptedRequestID)
+		}
+		w.WriteHeader(tc.ResponseCode)
+		w.Write(tc.Response)
+	}))
+	defer server.Close()
+
+	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		t.Error("Unexpected data API call")
+		w.WriteHeader(http.StatusNotFound)
+		w.Write([]byte(`{"message":"Not found"}`))
+	}))
+	defer dataServer.Close()
+
+	client, err := mockClient(server, dataServer)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var res *BasicResponse
+	for i, tc := range testCases {
+		currentTestIdx = i
+		t.Run(strconv.Itoa(i)+"/"+tc.Label, func(t *testing.T) {
+			timeoutCtx, cancelFn := context.WithTimeout(context.Background(), 1*time.Second)
+			defer cancelFn()
+			res, err = client.UpdateAudienceGroupDescription(tc.AudienceGroupID, tc.Description).WithContext(timeoutCtx).Do()
+			if tc.Want.Error != nil {
+				if !reflect.DeepEqual(err, tc.Want.Error) {
+					t.Errorf("Error %v; want %v", err, tc.Want.Error)
+				}
+			} else {
+				if err != nil {
+					t.Error(err)
+				}
+			}
+			if tc.Want.Response != nil {
+				if !reflect.DeepEqual(res, tc.Want.Response) {
+					t.Errorf("Response %v; want %v", res, tc.Want.Response)
+				}
+			}
+		})
+	}
+}
+
+// TestActivateAudienceGroup tests ActivateAudienceGroup
+func TestActivateAudienceGroup(t *testing.T) {
+	type RequestBody struct {
+	}
+	type want struct {
+		URLPath     string
+		RequestBody RequestBody
+		Response    *BasicResponse
+		Error       error
+	}
+	testCases := []struct {
+		Label             string
+		AudienceGroupID   int
+		RequestID         string
+		AcceptedRequestID string
+		ResponseCode      int
+		Response          []byte
+		Want              want
+	}{
+		{
+			Label:           "Activate Audience Fail",
+			AudienceGroupID: 1234567890123,
+			RequestID:       "12222",
+			ResponseCode:    http.StatusBadRequest,
+			Response:        []byte(``),
+			Want: want{
+				URLPath:     fmt.Sprintf(APIAudienceGroupActivate, 1234567890123),
+				RequestBody: RequestBody{},
+				Error: &APIError{
+					Code: http.StatusBadRequest,
+				},
+			},
+		},
+		{
+			Label:           "Activate Audience",
+			AudienceGroupID: 1234567890123,
+			RequestID:       "12222",
+			ResponseCode:    http.StatusOK,
+			Response:        []byte(``),
+			Want: want{
+				URLPath:     fmt.Sprintf(APIAudienceGroupActivate, 1234567890123),
+				RequestBody: RequestBody{},
+				Response: &BasicResponse{
+					RequestID: "12222",
+				},
+			},
+		},
+	}
+
+	var currentTestIdx int
+	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		tc := testCases[currentTestIdx]
+		if r.Method != http.MethodPut {
+			t.Errorf("Method %s; want %s", r.Method, http.MethodPut)
+		}
+		if r.URL.Path != tc.Want.URLPath {
+			t.Errorf("URLPath %s; want %s", r.URL.Path, tc.Want.URLPath)
+		}
+		w.Header().Set("X-Line-Request-Id", tc.RequestID)
+		if tc.AcceptedRequestID != "" {
+			w.Header().Set("X-Line-Accepted-Request-Id", tc.AcceptedRequestID)
+		}
+		w.WriteHeader(tc.ResponseCode)
+		w.Write(tc.Response)
+	}))
+	defer server.Close()
+
+	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		t.Error("Unexpected data API call")
+		w.WriteHeader(http.StatusNotFound)
+		w.Write([]byte(`{"message":"Not found"}`))
+	}))
+	defer dataServer.Close()
+
+	client, err := mockClient(server, dataServer)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var res *BasicResponse
+	for i, tc := range testCases {
+		currentTestIdx = i
+		t.Run(strconv.Itoa(i)+"/"+tc.Label, func(t *testing.T) {
+			timeoutCtx, cancelFn := context.WithTimeout(context.Background(), 1*time.Second)
+			defer cancelFn()
+			res, err = client.ActivateAudienceGroup(tc.AudienceGroupID).WithContext(timeoutCtx).Do()
+			if tc.Want.Error != nil {
+				if !reflect.DeepEqual(err, tc.Want.Error) {
+					t.Errorf("Error %v; want %v", err, tc.Want.Error)
+				}
+			} else {
+				if err != nil {
+					t.Error(err)
+				}
+			}
+			if tc.Want.Response != nil {
+				if !reflect.DeepEqual(res, tc.Want.Response) {
+					t.Errorf("Response %v; want %v", res, tc.Want.Response)
+				}
+			}
+		})
+	}
+}
+
+// TestDeleteAudienceGroup tests DeleteAudienceGroup
+func TestDeleteAudienceGroup(t *testing.T) {
+	type RequestBody struct {
+	}
+	type want struct {
+		URLPath     string
+		RequestBody RequestBody
+		Response    *BasicResponse
+		Error       error
+	}
+	testCases := []struct {
+		Label             string
+		AudienceGroupID   int
+		RequestID         string
+		AcceptedRequestID string
+		ResponseCode      int
+		Response          []byte
+		Want              want
+	}{
+		{
+			Label:           "Delete Audience Fail",
+			AudienceGroupID: 1234567890123,
+			RequestID:       "12222",
+			ResponseCode:    http.StatusBadRequest,
+			Response:        []byte(``),
+			Want: want{
+				URLPath:     fmt.Sprintf(APIAudienceGroup, 1234567890123),
+				RequestBody: RequestBody{},
+				Error: &APIError{
+					Code: http.StatusBadRequest,
+				},
+			},
+		},
+		{
+			Label:           "Delete Audience",
+			AudienceGroupID: 1234567890123,
+			RequestID:       "12222",
+			ResponseCode:    http.StatusOK,
+			Response:        []byte(``),
+			Want: want{
+				URLPath:     fmt.Sprintf(APIAudienceGroup, 1234567890123),
+				RequestBody: RequestBody{},
+				Response: &BasicResponse{
+					RequestID: "12222",
+				},
+			},
+		},
+	}
+
+	var currentTestIdx int
+	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		tc := testCases[currentTestIdx]
+		if r.Method != http.MethodDelete {
+			t.Errorf("Method %s; want %s", r.Method, http.MethodDelete)
+		}
+		if r.URL.Path != tc.Want.URLPath {
+			t.Errorf("URLPath %s; want %s", r.URL.Path, tc.Want.URLPath)
+		}
+		w.Header().Set("X-Line-Request-Id", tc.RequestID)
+		if tc.AcceptedRequestID != "" {
+			w.Header().Set("X-Line-Accepted-Request-Id", tc.AcceptedRequestID)
+		}
+		w.WriteHeader(tc.ResponseCode)
+		w.Write(tc.Response)
+	}))
+	defer server.Close()
+
+	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		t.Error("Unexpected data API call")
+		w.WriteHeader(http.StatusNotFound)
+		w.Write([]byte(`{"message":"Not found"}`))
+	}))
+	defer dataServer.Close()
+
+	client, err := mockClient(server, dataServer)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var res *BasicResponse
+	for i, tc := range testCases {
+		currentTestIdx = i
+		t.Run(strconv.Itoa(i)+"/"+tc.Label, func(t *testing.T) {
+			timeoutCtx, cancelFn := context.WithTimeout(context.Background(), 1*time.Second)
+			defer cancelFn()
+			res, err = client.DeleteAudienceGroup(tc.AudienceGroupID).WithContext(timeoutCtx).Do()
+			if tc.Want.Error != nil {
+				if !reflect.DeepEqual(err, tc.Want.Error) {
+					t.Errorf("Error %v; want %v", err, tc.Want.Error)
+				}
+			} else {
+				if err != nil {
+					t.Error(err)
+				}
+			}
+			if tc.Want.Response != nil {
+				if !reflect.DeepEqual(res, tc.Want.Response) {
+					t.Errorf("Response %v; want %v", res, tc.Want.Response)
+				}
+			}
+		})
+	}
+}
+
+// TestGetAudienceGroup tests GetAudienceGroup
+func TestGetAudienceGroup(t *testing.T) {
+	type RequestBody struct {
+	}
+	type want struct {
+		URLPath     string
+		RequestBody RequestBody
+		Response    *GetAudienceGroupResponse
+		Error       error
+	}
+	testCases := []struct {
+		Label             string
+		AudienceGroupID   int
+		RequestID         string
+		AcceptedRequestID string
+		ResponseCode      int
+		Response          []byte
+		Want              want
+	}{
+		{
+			Label:           "Get Audience Fail",
+			AudienceGroupID: 1234567890123,
+			RequestID:       "12222",
+			ResponseCode:    http.StatusBadRequest,
+			Response:        []byte(`{"message":"audience group not found","details":[{"message":"AUDIENCE_GROUP_NOT_FOUND","property":""}],"error":"","error_description":""}`),
+			Want: want{
+				URLPath:     fmt.Sprintf(APIAudienceGroup, 1234567890123),
+				RequestBody: RequestBody{},
+				Error: &APIError{
+					Code: http.StatusBadRequest,
+					Response: &ErrorResponse{
+						Message: "audience group not found",
+						Details: []errorResponseDetail{
+							{
+								Message: "AUDIENCE_GROUP_NOT_FOUND",
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			Label:           "Get UPLOAD Audience",
+			AudienceGroupID: 1234567890123,
+			RequestID:       "12222",
+			ResponseCode:    http.StatusOK,
+			Response:        []byte(`{"audienceGroup":{"audienceGroupId":1234567890123,"createRoute":"OA_MANAGER","type":"UPLOAD","description":"audienceGroupName_01","status":"READY","audienceCount":1887,"created":1608617466,"permission":"READ","expireTimestamp":1624342266},"jobs":[{"audienceGroupJobId":12345678,"audienceGroupId":1234567890123,"description":"audience_list.txt","type":"DIFF_ADD","status":"FINISHED","created":1608617472,"jobStatus":"FINISHED"}]}`),
+			Want: want{
+				URLPath:     fmt.Sprintf(APIAudienceGroup, 1234567890123),
+				RequestBody: RequestBody{},
+				Response: &GetAudienceGroupResponse{
+					RequestID: "12222",
+					AudienceGroup: AudienceGroup{
+						AudienceGroupID: 1234567890123,
+						CreateRoute:     "OA_MANAGER",
+						Type:            "UPLOAD",
+						Description:     "audienceGroupName_01",
+						Status:          "READY",
+						AudienceCount:   1887,
+						Created:         1608617466,
+						Permission:      "READ",
+						IsIfaAudience:   false,
+						ExpireTimestamp: 1624342266,
+					},
+					Jobs: []Job{
+						{
+							AudienceGroupJobID: 12345678,
+							AudienceGroupID:    1234567890123,
+							Description:        "audience_list.txt",
+							Type:               "DIFF_ADD",
+							Status:             "FINISHED",
+							AudienceCount:      0,
+							Created:            1608617472,
+							JobStatus:          "FINISHED",
+						},
+					},
+				},
+			},
+		},
+		{
+			Label:           "Get CLICK Audience",
+			AudienceGroupID: 1234567890987,
+			RequestID:       "12222",
+			ResponseCode:    http.StatusOK,
+			Response:        []byte(`{"audienceGroup":{"audienceGroupId":1234567890987,"createRoute":"OA_MANAGER","type":"CLICK","description":"audienceGroupName_02","status":"IN_PROGRESS","audienceCount":8619,"created":1611114828,"permission":"READ","requestId":"c10c3d86-f565-...","clickUrl":"https://example.com/","expireTimestamp":1624342266}}`),
+			Want: want{
+				URLPath:     fmt.Sprintf(APIAudienceGroup, 1234567890987),
+				RequestBody: RequestBody{},
+				Response: &GetAudienceGroupResponse{
+					RequestID: "12222",
+					AudienceGroup: AudienceGroup{
+						AudienceGroupID: 1234567890987,
+						CreateRoute:     "OA_MANAGER",
+						Type:            "CLICK",
+						Description:     "audienceGroupName_02",
+						Status:          "IN_PROGRESS",
+						AudienceCount:   8619,
+						Created:         1611114828,
+						Permission:      "READ",
+						IsIfaAudience:   false,
+						ExpireTimestamp: 1624342266,
+						RequestID:       "c10c3d86-f565-...",
+						ClickURL:        "https://example.com/",
+					},
+				},
+			},
+		},
+		{
+			Label:           "Get APP_EVENT Audience",
+			AudienceGroupID: 2345678909876,
+			RequestID:       "12222",
+			ResponseCode:    http.StatusOK,
+			Response:        []byte(`{"audienceGroup":{"audienceGroupId":2345678909876,"createRoute":"AD_MANAGER","type":"APP_EVENT","description":"audienceGroupName_03","status":"READY","audienceCount":8619,"created":1608619802,"permission":"READ","activated":1610068515,"inactivatedTimestamp":1625620516}}`),
+			Want: want{
+				URLPath:     fmt.Sprintf(APIAudienceGroup, 2345678909876),
+				RequestBody: RequestBody{},
+				Response: &GetAudienceGroupResponse{
+					RequestID: "12222",
+					AudienceGroup: AudienceGroup{
+						AudienceGroupID:      2345678909876,
+						CreateRoute:          "AD_MANAGER",
+						Type:                 "APP_EVENT",
+						Description:          "audienceGroupName_03",
+						Status:               "READY",
+						AudienceCount:        8619,
+						Created:              1608619802,
+						Permission:           "READ",
+						Activated:            1610068515,
+						InactivatedTimestamp: 1625620516,
+						IsIfaAudience:        false,
+					},
+				},
+			},
+		},
+	}
+
+	var currentTestIdx int
+	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		tc := testCases[currentTestIdx]
+		if r.Method != http.MethodGet {
+			t.Errorf("Method %s; want %s", r.Method, http.MethodGet)
+		}
+		if r.URL.Path != tc.Want.URLPath {
+			t.Errorf("URLPath %s; want %s", r.URL.Path, tc.Want.URLPath)
+		}
+		w.Header().Set("X-Line-Request-Id", tc.RequestID)
+		if tc.AcceptedRequestID != "" {
+			w.Header().Set("X-Line-Accepted-Request-Id", tc.AcceptedRequestID)
+		}
+		w.WriteHeader(tc.ResponseCode)
+		w.Write(tc.Response)
+	}))
+	defer server.Close()
+
+	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		t.Error("Unexpected data API call")
+		w.WriteHeader(http.StatusNotFound)
+		w.Write([]byte(`{"message":"Not found"}`))
+	}))
+	defer dataServer.Close()
+
+	client, err := mockClient(server, dataServer)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var res *GetAudienceGroupResponse
+	for i, tc := range testCases {
+		currentTestIdx = i
+		t.Run(strconv.Itoa(i)+"/"+tc.Label, func(t *testing.T) {
+			timeoutCtx, cancelFn := context.WithTimeout(context.Background(), 1*time.Second)
+			defer cancelFn()
+			res, err = client.GetAudienceGroup(tc.AudienceGroupID).WithContext(timeoutCtx).Do()
+			if tc.Want.Error != nil {
+				if !reflect.DeepEqual(err, tc.Want.Error) {
+					t.Errorf("Error %v; want %v", err, tc.Want.Error)
+				}
+			} else {
+				if err != nil {
+					t.Error(err)
+				}
+			}
+			if tc.Want.Response != nil {
+				if !reflect.DeepEqual(res, tc.Want.Response) {
+					t.Errorf("Response %v; want %v", res, tc.Want.Response)
+				}
+			}
+		})
+	}
+}
+
+// TestListAudienceGroup tests ListAudienceGroup
+func TestListAudienceGroup(t *testing.T) {
+	type RequestBody struct {
+	}
+	type RequestParameter struct {
+		Page                         int
+		Description                  string
+		Status                       AudienceStatusType
+		Size                         int
+		IncludesExternalPublicGroups bool
+		CreateRoute                  string
+	}
+	type want struct {
+		URLPath          string
+		RequestBody      RequestBody
+		RequestParameter RequestParameter
+		Response         *ListAudienceGroupResponse
+		Error            error
+	}
+	testCases := []struct {
+		Label                        string
+		Page                         int
+		Description                  string
+		Status                       AudienceStatusType
+		Size                         int
+		IncludesExternalPublicGroups bool
+		CreateRoute                  string
+		RequestID                    string
+		AcceptedRequestID            string
+		ResponseCode                 int
+		Response                     []byte
+		Want                         want
+	}{
+		{
+			Label:        "List Audience Fail",
+			Page:         1,
+			Description:  "audienceGroupName",
+			Status:       INPROGRESS,
+			Size:         41,
+			CreateRoute:  "OA_MANAGER",
+			RequestID:    "12222",
+			ResponseCode: http.StatusBadRequest,
+			Response:     []byte(`{"message":"size: must be less than or equal to 40","details":[{"message":"TOO_HIGH","property":""}],"error":"","error_description":""}`),
+			Want: want{
+				URLPath:     APIAudienceGroupList,
+				RequestBody: RequestBody{},
+				RequestParameter: RequestParameter{
+					Page:        1,
+					Description: "audienceGroupName",
+					Status:      INPROGRESS,
+					Size:        41,
+					CreateRoute: "OA_MANAGER",
+				},
+				Error: &APIError{
+					Code: http.StatusBadRequest,
+					Response: &ErrorResponse{
+						Message: "size: must be less than or equal to 40",
+						Details: []errorResponseDetail{
+							{
+								Message: "TOO_HIGH",
+							},
+						},
+					},
+				},
+			},
+		},
+		{
+			Label:        "List Audience",
+			Page:         1,
+			Description:  "audienceGroupName",
+			Size:         40,
+			CreateRoute:  "OA_MANAGER",
+			RequestID:    "12222",
+			ResponseCode: http.StatusOK,
+			Response:     []byte(`{"audienceGroups":[{"audienceGroupId":1234567890123,"createRoute":"OA_MANAGER","type":"CLICK","description":"audienceGroupName_01","status":"IN_PROGRESS","audienceCount":8619,"created":1611114828,"permission":"READ","requestId":"c10c3d86-f565-...","clickUrl":"https://example.com/","expireTimestamp":1626753228},{"audienceGroupId":2345678901234,"createRoute":"AD_MANAGER","type":"APP_EVENT","description":"audienceGroupName_02","status":"READY","audienceCount":3368,"created":1608619802,"permission":"READ","activated":1610068515,"inactivatedTimestamp":1625620516}],"totalCount":2,"page":40,"size":1}`),
+			Want: want{
+				URLPath:     APIAudienceGroupList,
+				RequestBody: RequestBody{},
+				RequestParameter: RequestParameter{
+					Page:        1,
+					Description: "audienceGroupName",
+					Size:        40,
+					CreateRoute: "OA_MANAGER",
+				},
+				Response: &ListAudienceGroupResponse{
+					RequestID: "12222",
+					AudienceGroups: []AudienceGroup{
+						{
+							AudienceGroupID: 1234567890123,
+							CreateRoute:     "OA_MANAGER",
+							Type:            "CLICK",
+							Description:     "audienceGroupName_01",
+							Status:          "IN_PROGRESS",
+							AudienceCount:   8619,
+							Created:         1611114828,
+							Permission:      "READ",
+							IsIfaAudience:   false,
+							ExpireTimestamp: 1626753228,
+							RequestID:       "c10c3d86-f565-...",
+							ClickURL:        "https://example.com/",
+						},
+						{
+							AudienceGroupID:      2345678901234,
+							CreateRoute:          "AD_MANAGER",
+							Type:                 "APP_EVENT",
+							Description:          "audienceGroupName_02",
+							Status:               "READY",
+							AudienceCount:        3368,
+							Created:              1608619802,
+							Permission:           "READ",
+							IsIfaAudience:        false,
+							Activated:            1610068515,
+							InactivatedTimestamp: 1625620516,
+						},
+					},
+					HasNextPage:                      false,
+					TotalCount:                       2,
+					ReadWriteAudienceGroupTotalCount: 0,
+					Page:                             40,
+					Size:                             1,
+				},
+			},
+		},
+		{
+			Label:                        "List Audience no data",
+			Page:                         1,
+			Description:                  "audienceGroupName",
+			IncludesExternalPublicGroups: false,
+			Size:                         40,
+			CreateRoute:                  "OA_MANAGER",
+			RequestID:                    "12222",
+			ResponseCode:                 http.StatusOK,
+			Response:                     []byte(`{"page":40,"size":1}`),
+			Want: want{
+				URLPath:     APIAudienceGroupList,
+				RequestBody: RequestBody{},
+				RequestParameter: RequestParameter{
+					Page:                         1,
+					Description:                  "audienceGroupName",
+					IncludesExternalPublicGroups: false,
+					Size:                         40,
+					CreateRoute:                  "OA_MANAGER",
+				},
+				Response: &ListAudienceGroupResponse{
+					RequestID:                        "12222",
+					HasNextPage:                      false,
+					TotalCount:                       0,
+					ReadWriteAudienceGroupTotalCount: 0,
+					Page:                             40,
+					Size:                             1,
+				},
+			},
+		},
+	}
+
+	var currentTestIdx int
+	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		tc := testCases[currentTestIdx]
+		if r.Method != http.MethodGet {
+			t.Errorf("Method %s; want %s", r.Method, http.MethodGet)
+		}
+		if r.URL.Path != tc.Want.URLPath {
+			t.Errorf("URLPath %s; want %s", r.URL.Path, tc.Want.URLPath)
+		}
+
+		if v := r.URL.Query().Get("page"); v != "" {
+			page, err := strconv.ParseInt(v, 10, 64)
+			if err != nil {
+				t.Fatal(err)
+			}
+			if int(page) != tc.Want.RequestParameter.Page {
+				t.Errorf("Request Page %v; want %v", page, tc.Want.RequestParameter.Page)
+			}
+		}
+		if v := r.URL.Query().Get("size"); v != "" {
+			size, err := strconv.ParseInt(v, 10, 64)
+			if err != nil {
+				t.Fatal(err)
+			}
+			if int(size) != tc.Want.RequestParameter.Size {
+				t.Errorf("Request Size %v; want %v", size, tc.Want.RequestParameter.Size)
+			}
+		}
+		if v := r.URL.Query().Get("description"); v != "" {
+			if v != tc.Want.RequestParameter.Description {
+				t.Errorf("Request Description %v; want %v", v, tc.Want.RequestParameter.Description)
+			}
+		}
+		if v := r.URL.Query().Get("status"); v != "" {
+			if v != tc.Want.RequestParameter.Status.String() {
+				t.Errorf("Request Status %v; want %v", v, tc.Want.RequestParameter.Status)
+			}
+		}
+		if v := r.URL.Query().Get("includesExternalPublicGroups"); v != "" {
+			includesExternalPublicGroups, err := strconv.ParseBool(v)
+			if err != nil {
+				t.Fatal(err)
+			}
+			if includesExternalPublicGroups != tc.Want.RequestParameter.IncludesExternalPublicGroups {
+				t.Errorf("Request IncludesExternalPublicGroups %v; want %v", v, tc.Want.RequestParameter.IncludesExternalPublicGroups)
+			}
+		}
+		if v := r.URL.Query().Get("createRoute"); v != "" {
+			if v != tc.Want.RequestParameter.CreateRoute {
+				t.Errorf("Request CreateRoute %v; want %v", v, tc.Want.RequestParameter.CreateRoute)
+			}
+		}
+
+		w.Header().Set("X-Line-Request-Id", tc.RequestID)
+		if tc.AcceptedRequestID != "" {
+			w.Header().Set("X-Line-Accepted-Request-Id", tc.AcceptedRequestID)
+		}
+		w.WriteHeader(tc.ResponseCode)
+		w.Write(tc.Response)
+	}))
+	defer server.Close()
+
+	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		t.Error("Unexpected data API call")
+		w.WriteHeader(http.StatusNotFound)
+		w.Write([]byte(`{"message":"Not found"}`))
+	}))
+	defer dataServer.Close()
+
+	client, err := mockClient(server, dataServer)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var res *ListAudienceGroupResponse
+	for i, tc := range testCases {
+		currentTestIdx = i
+		t.Run(strconv.Itoa(i)+"/"+tc.Label, func(t *testing.T) {
+			var options []IListAudienceGroupOption
+			if tc.Description != "" {
+				options = append(options, WithListAudienceGroupCallDescription(tc.Description))
+			}
+			if tc.Status != "" {
+				options = append(options, WithListAudienceGroupCallStatus(tc.Status))
+			}
+			if !tc.IncludesExternalPublicGroups {
+				options = append(options, WithListAudienceGroupCallIncludesExternalPublicGroups(tc.IncludesExternalPublicGroups))
+			}
+			if tc.Size > 0 {
+				options = append(options, WithListAudienceGroupCallSize(tc.Size))
+			}
+			if tc.CreateRoute != "" {
+				options = append(options, WithListAudienceGroupCallCreateRoute(tc.CreateRoute))
+			}
+
+			timeoutCtx, cancelFn := context.WithTimeout(context.Background(), 1*time.Second)
+			defer cancelFn()
+			res, err = client.ListAudienceGroup(tc.Page, options...).WithContext(timeoutCtx).Do()
+			if tc.Want.Error != nil {
+				if !reflect.DeepEqual(err, tc.Want.Error) {
+					t.Errorf("Error %v; want %v", err, tc.Want.Error)
+				}
+			} else {
+				if err != nil {
+					t.Error(err)
+				}
+			}
+			if tc.Want.Response != nil {
+				if !reflect.DeepEqual(res, tc.Want.Response) {
+					t.Errorf("Response %v; want %v", res, tc.Want.Response)
+				}
+			}
+		})
+	}
+}
+
+// TestGetAudienceGroupAuthorityLevel tests GetAudienceGroupAuthorityLevel
+func TestGetAudienceGroupAuthorityLevel(t *testing.T) {
+	type RequestBody struct {
+	}
+	type want struct {
+		URLPath     string
+		RequestBody RequestBody
+		Response    *GetAudienceGroupAuthorityLevelResponse
+		Error       error
+	}
+	testCases := []struct {
+		Label             string
+		RequestID         string
+		AcceptedRequestID string
+		ResponseCode      int
+		Response          []byte
+		Want              want
+	}{
+		{
+			Label:        "Get Audience AuthorityLevel Fail",
+			RequestID:    "12222",
+			ResponseCode: http.StatusBadRequest,
+			Response:     []byte(``),
+			Want: want{
+				URLPath:     APIAudienceGroupAuthorityLevel,
+				RequestBody: RequestBody{},
+				Error: &APIError{
+					Code: http.StatusBadRequest,
+				},
+			},
+		},
+		{
+			Label:        "Get Audience AuthorityLevel",
+			RequestID:    "12222",
+			ResponseCode: http.StatusOK,
+			Response:     []byte(`{"authorityLevel":"PUBLIC"}`),
+			Want: want{
+				URLPath:     APIAudienceGroupAuthorityLevel,
+				RequestBody: RequestBody{},
+				Response: &GetAudienceGroupAuthorityLevelResponse{
+					RequestID:      "12222",
+					AuthorityLevel: PUBLIC,
+				},
+			},
+		},
+	}
+
+	var currentTestIdx int
+	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		tc := testCases[currentTestIdx]
+		if r.Method != http.MethodGet {
+			t.Errorf("Method %s; want %s", r.Method, http.MethodGet)
+		}
+		if r.URL.Path != tc.Want.URLPath {
+			t.Errorf("URLPath %s; want %s", r.URL.Path, tc.Want.URLPath)
+		}
+		w.Header().Set("X-Line-Request-Id", tc.RequestID)
+		if tc.AcceptedRequestID != "" {
+			w.Header().Set("X-Line-Accepted-Request-Id", tc.AcceptedRequestID)
+		}
+		w.WriteHeader(tc.ResponseCode)
+		w.Write(tc.Response)
+	}))
+	defer server.Close()
+
+	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		t.Error("Unexpected data API call")
+		w.WriteHeader(http.StatusNotFound)
+		w.Write([]byte(`{"message":"Not found"}`))
+	}))
+	defer dataServer.Close()
+
+	client, err := mockClient(server, dataServer)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var res *GetAudienceGroupAuthorityLevelResponse
+	for i, tc := range testCases {
+		currentTestIdx = i
+		t.Run(strconv.Itoa(i)+"/"+tc.Label, func(t *testing.T) {
+			timeoutCtx, cancelFn := context.WithTimeout(context.Background(), 1*time.Second)
+			defer cancelFn()
+			res, err = client.GetAudienceGroupAuthorityLevel().WithContext(timeoutCtx).Do()
+			if tc.Want.Error != nil {
+				log.Println(err)
+				log.Println(tc.Want.Error)
+				if !reflect.DeepEqual(err, tc.Want.Error) {
+					t.Errorf("Error %v; want %v", err, tc.Want.Error)
+				}
+			} else {
+				if err != nil {
+					t.Error(err)
+				}
+			}
+			if tc.Want.Response != nil {
+				if !reflect.DeepEqual(res, tc.Want.Response) {
+					t.Errorf("Response %v; want %v", res, tc.Want.Response)
+				}
+			}
+		})
+	}
+}
+
+// TestChangeAudienceGroupAuthorityLevel tests ChangeAudienceGroupAuthorityLevel
+func TestChangeAudienceGroupAuthorityLevel(t *testing.T) {
+	type RequestBody struct {
+		AuthorityLevel AudienceAuthorityLevelType
+	}
+	type want struct {
+		URLPath     string
+		RequestBody RequestBody
+		Response    *BasicResponse
+		Error       error
+	}
+	testCases := []struct {
+		Label             string
+		AuthorityLevel    AudienceAuthorityLevelType
+		RequestID         string
+		AcceptedRequestID string
+		ResponseCode      int
+		Response          []byte
+		Want              want
+	}{
+		{
+			Label:          "Change Audience AuthorityLevel Fail",
+			AuthorityLevel: PUBLIC,
+			RequestID:      "12222",
+			ResponseCode:   http.StatusBadRequest,
+			Response:       []byte(``),
+			Want: want{
+				URLPath: APIAudienceGroupAuthorityLevel,
+				RequestBody: RequestBody{
+					AuthorityLevel: PUBLIC,
+				},
+				Error: &APIError{
+					Code: http.StatusBadRequest,
+				},
+			},
+		},
+		{
+			Label:          "Change Audience AuthorityLevel ",
+			AuthorityLevel: PUBLIC,
+			RequestID:      "12222",
+			ResponseCode:   http.StatusOK,
+			Response:       []byte(``),
+			Want: want{
+				URLPath: APIAudienceGroupAuthorityLevel,
+				RequestBody: RequestBody{
+					AuthorityLevel: PUBLIC,
+				},
+				Response: &BasicResponse{
+					RequestID: "12222",
+				},
+			},
+		},
+	}
+
+	var currentTestIdx int
+	server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		tc := testCases[currentTestIdx]
+		if r.Method != http.MethodPut {
+			t.Errorf("Method %s; want %s", r.Method, http.MethodPut)
+		}
+		if r.URL.Path != tc.Want.URLPath {
+			t.Errorf("URLPath %s; want %s", r.URL.Path, tc.Want.URLPath)
+		}
+
+		var result RequestBody
+		if err := json.NewDecoder(r.Body).Decode(&result); err != nil {
+			t.Fatal(err)
+		}
+		if !reflect.DeepEqual(result, tc.Want.RequestBody) {
+			t.Errorf("Request %v; want %v", result, tc.Want.RequestBody)
+		}
+
+		w.Header().Set("X-Line-Request-Id", tc.RequestID)
+		if tc.AcceptedRequestID != "" {
+			w.Header().Set("X-Line-Accepted-Request-Id", tc.AcceptedRequestID)
+		}
+		w.WriteHeader(tc.ResponseCode)
+		w.Write(tc.Response)
+	}))
+	defer server.Close()
+
+	dataServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		defer r.Body.Close()
+		t.Error("Unexpected data API call")
+		w.WriteHeader(http.StatusNotFound)
+		w.Write([]byte(`{"message":"Not found"}`))
+	}))
+	defer dataServer.Close()
+
+	client, err := mockClient(server, dataServer)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	var res *BasicResponse
+	for i, tc := range testCases {
+		currentTestIdx = i
+		t.Run(strconv.Itoa(i)+"/"+tc.Label, func(t *testing.T) {
+			timeoutCtx, cancelFn := context.WithTimeout(context.Background(), 1*time.Second)
+			defer cancelFn()
+			res, err = client.ChangeAudienceGroupAuthorityLevel(tc.AuthorityLevel).WithContext(timeoutCtx).Do()
+			if tc.Want.Error != nil {
+				log.Println(err)
+				log.Println(tc.Want.Error)
+				if !reflect.DeepEqual(err, tc.Want.Error) {
+					t.Errorf("Error %v; want %v", err, tc.Want.Error)
+				}
+			} else {
+				if err != nil {
+					t.Error(err)
+				}
+			}
+			if tc.Want.Response != nil {
+				if !reflect.DeepEqual(res, tc.Want.Response) {
+					t.Errorf("Response %v; want %v", res, tc.Want.Response)
+				}
+			}
+		})
+	}
+}