ronantakizawa/codereview-bench huggingface dataset:
Repository: go-gitea/gitea
File: web_src/js/markup/render-iframe.ts (TypeScript)

Code before:
import {generateElemId, queryElemChildren} from '../utils/dom.ts';
import {isDarkTheme} from '../utils.ts';

export async function loadRenderIframeContent(iframe: HTMLIFrameElement) {
  const iframeSrcUrl = iframe.getAttribute('data-src');
  if (!iframe.id) iframe.id = generateElemId('gitea-iframe-');

  window.addEventListener('message', (e) => {
    if (e.data && e.data.giteaIframeCmd === 'resize' && e.data.giteaIframeId === iframe.id) {
      iframe.style.height = `${e.data.giteaIframeHeight}px`;
    }
  });

  let urlParams = '';
  urlParams += `&gitea-is-dark-theme=${isDarkTheme()}`;
  urlParams += `&gitea-iframe-id=${iframe.id}`;
  iframe.src = iframeSrcUrl + (iframeSrcUrl.includes('?') ? '&' : '?') + urlParams.substring(1);
}

export function initMarkupRenderIframe(el: HTMLElement) {
  queryElemChildren(el, 'iframe.external-render-iframe', loadRenderIframeContent);
}


Review comment (suggestion):
Wouldn't the same mechanism we use in the mermaid iframe work to update the height correctly?

https://github.com/go-gitea/gitea/blob/195fc715ff6a8c67c1b3473316b52379bfca6d49/web_src/js/markup/mermaid.ts#L60

That and the `IntersectionObserver`.

Code after:
import {generateElemId, queryElemChildren} from '../utils/dom.ts';
import {isDarkTheme} from '../utils.ts';

export async function loadRenderIframeContent(iframe: HTMLIFrameElement) {
  const iframeSrcUrl = iframe.getAttribute('data-src');
  if (!iframe.id) iframe.id = generateElemId('gitea-iframe-');

  window.addEventListener('message', (e) => {
    if (!e.data?.giteaIframeCmd || e.data?.giteaIframeId !== iframe.id) return;
    const cmd = e.data.giteaIframeCmd;
    if (cmd === 'resize') {
      iframe.style.height = `${e.data.iframeHeight}px`;
    } else if (cmd === 'open-link') {
      if (e.data.anchorTarget === '_blank') {
        window.open(e.data.openLink, '_blank');
      } else {
        window.location.href = e.data.openLink;
      }
    } else {
      throw new Error(`Unknown gitea iframe cmd: ${cmd}`);
    }
  });

  const u = new URL(iframeSrcUrl, window.location.origin);
  u.searchParams.set('gitea-is-dark-theme', String(isDarkTheme()));
  u.searchParams.set('gitea-iframe-id', iframe.id);
  iframe.src = u.href;
}

export function initMarkupRenderIframe(el: HTMLElement) {
  queryElemChildren(el, 'iframe.external-render-iframe', loadRenderIframeContent);
}
Repository: go-gitea/gitea
File: web_src/js/markup/render-iframe.ts (TypeScript)

Code before:

export async function loadRenderIframeContent(iframe: HTMLIFrameElement) {
  const iframeSrcUrl = iframe.getAttribute('data-src');
  if (!iframe.id) iframe.id = generateElemId('gitea-iframe-');

  window.addEventListener('message', (e) => {
    if (!e.data?.giteaIframeCmd || e.data?.giteaIframeId !== iframe.id) return;
    const cmd = e.data.giteaIframeCmd;
    if (cmd === 'resize') {
      iframe.style.height = `${e.data.iframeHeight}px`;
    } else if (cmd === 'open-link') {
      if (e.data.anchorTarget === '_blank') {
        window.open(e.data.openLink, '_blank');
      } else {
        window.location.href = e.data.openLink;
      }
    } else {
      throw new Error(`Unknown gitea iframe cmd: ${cmd}`);
    }
  });
  iframe.src = iframeSrcUrl + (iframeSrcUrl.includes('?') ? '&' : '?') + String(new URLSearchParams([
    ['gitea-is-dark-theme', String(isDarkTheme())],
    ['gitea-iframe-id', iframe.id],
  ]));
}

export function initMarkupRenderIframe(el: HTMLElement) {
  queryElemChildren(el, 'iframe.external-render-iframe', loadRenderIframeContent);
}


Review comment (suggestion):
`iframeSrcUrl.includes('?') ? '&' : '?'` is brittle, I would create and modify a `URL`:

```ts
iframeUrl = new URL(iframeSrcUrl);
iframeUrl.seachParams.set('gitea-is-dark-theme', String(isDarkTheme());
iframeUrl.seachParams.set('gitea-iframe-id', iframe.id);
iframe.src = iframeUrl.href;

Code after:

export async function loadRenderIframeContent(iframe: HTMLIFrameElement) {
  const iframeSrcUrl = iframe.getAttribute('data-src');
  if (!iframe.id) iframe.id = generateElemId('gitea-iframe-');

  window.addEventListener('message', (e) => {
    if (!e.data?.giteaIframeCmd || e.data?.giteaIframeId !== iframe.id) return;
    const cmd = e.data.giteaIframeCmd;
    if (cmd === 'resize') {
      iframe.style.height = `${e.data.iframeHeight}px`;
    } else if (cmd === 'open-link') {
      if (e.data.anchorTarget === '_blank') {
        window.open(e.data.openLink, '_blank');
      } else {
        window.location.href = e.data.openLink;
      }
    } else {
      throw new Error(`Unknown gitea iframe cmd: ${cmd}`);
    }
  });

  const u = new URL(iframeSrcUrl, window.location.origin);
  u.searchParams.set('gitea-is-dark-theme', String(isDarkTheme()));
  u.searchParams.set('gitea-iframe-id', iframe.id);
  iframe.src = u.href;
}

export function initMarkupRenderIframe(el: HTMLElement) {
  queryElemChildren(el, 'iframe.external-render-iframe', loadRenderIframeContent);
}
Repository: go-gitea/gitea
File: web_src/js/components/ViewFileTreeStore.ts (TypeScript)

Code before:

    async loadViewContent(url: string) {
      url = url.includes('?') ? url.replace('?', '?only_content=true') : `${url}?only_content=true`;
      const response = await GET(url);
      document.querySelector('.repo-view-content').innerHTML = await response.text();
    },

    async navigateTreeView(treePath: string) {
      const url = store.buildTreePathWebUrl(treePath);
      const title = store.buildTitle(store.selectedItem, treePath);
      window.history.pushState({treePath, url}, null, url);
      store.selectedItem = treePath;
      await store.loadViewContent(url);
      document.title = title;
    },

    buildTreePathWebUrl(treePath: string) {
      return `${props.repoLink}/src/${props.currentRefNameSubURL}/${pathEscapeSegments(treePath)}`;
    },

    buildTitle(oldTreePath: string, treePath: string) {
      // the title always starts with "<repoName>/<treePath>"
      const oldPrefixLength = props.repoName.length + 1 + oldTreePath.length;
      const titleSuffix = document.title.substring(oldPrefixLength);
      return `${props.repoName}/${treePath}${titleSuffix}`;
    },
  });
  return store;
}


Review comment (suggestion):
That's really fragile.

I think backend can fully prepare the "title" (`ctx.Data["Title"]`), pass it to frontend, and frontend only needs to set it.

Code after:

    async loadViewContent(url: string) {
      const u = new URL(url, window.origin);
      u.searchParams.set('only_content', 'true');
      const response = await GET(u.href);
      const elViewContent = document.querySelector('.repo-view-content');
      elViewContent.innerHTML = await response.text();
      const elViewContentData = elViewContent.querySelector('.repo-view-content-data');
      if (!elViewContentData) return; // if error occurs, there is no such element
      const t1 = elViewContentData.getAttribute('data-document-title');
      const t2 = elViewContentData.getAttribute('data-document-title-common');
      document.title = `${t1} - ${t2}`; // follow the format in head.tmpl: <head><title>...</title></head>
    },

    async navigateTreeView(treePath: string) {
      const url = store.buildTreePathWebUrl(treePath);
      window.history.pushState({treePath, url}, null, url);
      store.selectedItem = treePath;
      await store.loadViewContent(url);
    },

    buildTreePathWebUrl(treePath: string) {
      return `${props.repoLink}/src/${props.currentRefNameSubURL}/${pathEscapeSegments(treePath)}`;
    },
  });
  return store;
}
Repository: go-gitea/gitea
File: routers/api/v1/repo/branch.go (Go)

Code before:
	branchName := ctx.PathParam("*")
	repo := ctx.Repo.Repository

	if repo.IsEmpty {
		ctx.APIError(http.StatusNotFound, "Git Repository is empty.")
		return
	}

	if repo.IsMirror {
		ctx.APIError(http.StatusForbidden, "Git Repository is a mirror.")
		return
	}

	if ctx.Repo.GitRepo == nil {
		ctx.APIErrorInternal(nil)
		return
	}

	if err := repo_service.UpdateBranch(ctx, repo, ctx.Doer, branchName, opt.NewCommitID, opt.OldCommitID, opt.Force); err != nil {
		switch {
		case git_model.IsErrBranchNotExist(err):
			ctx.APIError(http.StatusNotFound, "Branch doesn't exist.")
		case repo_service.IsErrBranchCommitDoesNotMatch(err):
			ctx.APIError(http.StatusConflict, err)
		case git.IsErrPushOutOfDate(err):
			ctx.APIError(http.StatusConflict, "The update is not a fast-forward.")
		case git.IsErrPushRejected(err):
			rej := err.(*git.ErrPushRejected)
			ctx.APIError(http.StatusForbidden, rej.Message)
		case repo_model.IsErrUserDoesNotHaveAccessToRepo(err):
			ctx.APIError(http.StatusForbidden, err)
		case git.IsErrNotExist(err):
			ctx.APIError(http.StatusUnprocessableEntity, err)
		default:
			ctx.APIErrorInternal(err)
		}
		return
	}

	ctx.Status(http.StatusNoContent)
}

// RenameBranch renames a repository's branch.
func RenameBranch(ctx *context.APIContext) {
	// swagger:operation PATCH /repos/{owner}/{repo}/branches/{branch} repository repoRenameBranch
	// ---
	// summary: Rename a branch
	// consumes:
	// - application/json
	// produces:
	// - application/json

Review comment (suggestion):
Most errors should be able to unwrapped to `util.ErrXxx`, then the switch can be simplified and handle more cases.

Code after:
	branchName := ctx.PathParam("*")
	repo := ctx.Repo.Repository

	if repo.IsEmpty {
		ctx.APIError(http.StatusNotFound, "Git Repository is empty.")
		return
	}

	if repo.IsMirror {
		ctx.APIError(http.StatusForbidden, "Git Repository is a mirror.")
		return
	}

	// permission check has been done in api.go
	if err := repo_service.UpdateBranch(ctx, repo, ctx.Repo.GitRepo, ctx.Doer, branchName, opt.NewCommitID, opt.OldCommitID, opt.Force); err != nil {
		switch {
		case git_model.IsErrBranchNotExist(err):
			ctx.APIErrorNotFound(err)
		case errors.Is(err, util.ErrInvalidArgument):
			ctx.APIError(http.StatusUnprocessableEntity, err)
		case git.IsErrPushRejected(err):
			rej := err.(*git.ErrPushRejected)
			ctx.APIError(http.StatusForbidden, rej.Message)
		default:
			ctx.APIErrorInternal(err)
		}
		return
	}

	ctx.Status(http.StatusNoContent)
}

// RenameBranch renames a repository's branch.
func RenameBranch(ctx *context.APIContext) {
	// swagger:operation PATCH /repos/{owner}/{repo}/branches/{branch} repository repoRenameBranch
	// ---
	// summary: Rename a branch
	// consumes:
	// - application/json
	// produces:
	// - application/json
	// parameters:
	// - name: owner
	//   in: path
	//   description: owner of the repo
	//   type: string
	//   required: true
	// - name: repo
	//   in: path
	//   description: name of the repo
	//   type: string
Repository: go-gitea/gitea
File: services/repository/branch.go (Go)

Code before:
	if err := repo.MustNotBeArchived(); err != nil {
		return err
	}

	perm, err := access_model.GetUserRepoPermission(ctx, repo, doer)
	if err != nil {
		return err
	}
	if !perm.CanWrite(unit.TypeCode) {
		return repo_model.ErrUserDoesNotHaveAccessToRepo{
			UserID:   doer.ID,
			RepoName: repo.LowerName,
		}
	}

	gitRepo, err := gitrepo.OpenRepository(ctx, repo)
	if err != nil {
		return fmt.Errorf("OpenRepository: %w", err)
	}
	defer gitRepo.Close()

	branchCommit, err := gitRepo.GetBranchCommit(branchName)
	if err != nil {
		if git.IsErrNotExist(err) {
			return git_model.ErrBranchNotExist{RepoID: repo.ID, BranchName: branchName}
		}
		return err
	}
	currentCommitID := branchCommit.ID.String()

	if expectedOldCommitID != "" {
		expectedID, err := gitRepo.ConvertToGitID(expectedOldCommitID)
		if err != nil {
			return fmt.Errorf("ConvertToGitID(old): %w", err)
		}
		if expectedID.String() != currentCommitID {
			return ErrBranchCommitDoesNotMatch{Expected: currentCommitID, Given: expectedID.String()}
		}
	}

	newID, err := gitRepo.ConvertToGitID(newCommitID)
	if err != nil {
		return fmt.Errorf("ConvertToGitID(new): %w", err)
	}
	newCommit, err := gitRepo.GetCommit(newID.String())
	if err != nil {
		return err
	}

	if newCommit.ID.String() == currentCommitID {
		return nil

Review comment (suggestion):
`GetBranchCommit` already returns the wrapped error, the new `if` check doesn't really help

Code after:
		return err
	}
	if branch.IsDeleted {
		return git_model.ErrBranchNotExist{
			BranchName: branchName,
		}
	}

	if expectedOldCommitID != "" {
		expectedID, err := gitRepo.ConvertToGitID(expectedOldCommitID)
		if err != nil {
			return fmt.Errorf("ConvertToGitID(old): %w", err)
		}
		if expectedID.String() != branch.CommitID {
			return util.NewInvalidArgumentErrorf("branch commit does not match [expected: %s, given: %s]", expectedID.String(), branch.CommitID)
		}
	}

	newID, err := gitRepo.ConvertToGitID(newCommitID)
	if err != nil {
		return fmt.Errorf("ConvertToGitID(new): %w", err)
	}
	newCommit, err := gitRepo.GetCommit(newID.String())
	if err != nil {
		return err
	}

	if newCommit.ID.String() == branch.CommitID {
		return nil
	}

	isForcePush, err := newCommit.IsForcePush(branch.CommitID)
	if err != nil {
		return err
	}
	if isForcePush && !force {
		return util.NewInvalidArgumentErrorf("Force push %s need a confirm force parameter", branchName)
	}

	pushOpts := git.PushOptions{
		Remote: repo.RepoPath(),
		Branch: fmt.Sprintf("%s:%s%s", newCommit.ID.String(), git.BranchPrefix, branchName),
		Env:    repo_module.PushingEnvironment(doer, repo),
		Force:  isForcePush || force,
	}

	if expectedOldCommitID != "" {
		pushOpts.ForceWithLease = fmt.Sprintf("%s:%s", git.BranchPrefix+branchName, branch.CommitID)
	}

	// branch protection will be checked in the pre received hook, so that we don't need any check here
Repository: go-gitea/gitea
File: routers/api/packages/composer/composer.go (Go)

Code before:
			return
		}
		cp.Version = v.String()
	}

	_, _, err = packages_service.CreatePackageAndAddFile(
		ctx,
		&packages_service.PackageCreationInfo{
			PackageInfo: packages_service.PackageInfo{
				Owner:       ctx.Package.Owner,
				PackageType: packages_model.TypeComposer,
				Name:        cp.Name,
				Version:     cp.Version,
			},
			SemverCompatible: true,
			Creator:          ctx.Doer,
			Metadata:         cp.Metadata,
			VersionProperties: map[string]string{
				composer_module.TypeProperty: cp.Type,
			},
		},
		&packages_service.PackageFileCreationInfo{
			PackageFileInfo: packages_service.PackageFileInfo{
				Filename: cp.Filename,
			},
			Creator: ctx.Doer,
			Data:    buf,
			IsLead:  true,
		},
	)
	if err != nil {
		switch err {
		case packages_model.ErrDuplicatePackageVersion:
			apiError(ctx, http.StatusConflict, err)
		case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
			apiError(ctx, http.StatusForbidden, err)
		default:
			apiError(ctx, http.StatusInternalServerError, err)
		}
		return
	}

	ctx.Status(http.StatusCreated)
}


Review comment (suggestion):
Old code generated a name like `gitea-package..zip` if the "version" is empty (update: old code guarantees that the "version" can't be empty, so new code also follows the behavior)

Code after:
	_, _, err = packages_service.CreatePackageAndAddFile(
		ctx,
		&packages_service.PackageCreationInfo{
			PackageInfo: packages_service.PackageInfo{
				Owner:       ctx.Package.Owner,
				PackageType: packages_model.TypeComposer,
				Name:        cp.Name,
				Version:     cp.Version,
			},
			SemverCompatible: true,
			Creator:          ctx.Doer,
			Metadata:         cp.Metadata,
			VersionProperties: map[string]string{
				composer_module.TypeProperty: cp.Type,
			},
		},
		&packages_service.PackageFileCreationInfo{
			PackageFileInfo: packages_service.PackageFileInfo{
				Filename: cp.Filename,
			},
			Creator: ctx.Doer,
			Data:    buf,
			IsLead:  true,
		},
	)
	if err != nil {
		switch err {
		case packages_model.ErrDuplicatePackageVersion:
			apiError(ctx, http.StatusConflict, err)
		case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
			apiError(ctx, http.StatusForbidden, err)
		default:
			apiError(ctx, http.StatusInternalServerError, err)
		}
		return
	}

	ctx.Status(http.StatusCreated)
}
Repository: go-gitea/gitea
File: tests/integration/pull_merge_test.go (Go)

Code before:
						commitSummary: "Refactor user service",
						commitMessage: `Implement the login endpoint.
Validate request body.`,
					},
					{
						userName:      user2.Name,
						commitSummary: "Add email notification service",
						commitMessage: `Implements a new email notification module.

- Supports templating
- Supports HTML and plain text modes
- Includes retry logic`,
					},
				},
				expectedMessage: `* Refactor user service

Implement the login endpoint.
Validate request body.

* Add email notification service

Implements a new email notification module.

- Supports templating
- Supports HTML and plain text modes
- Includes retry logic

`,
			},
			{
				name: "Long Message",
				commitInfos: []*commitInfo{
					{
						userName:      user2.Name,
						commitSummary: "Add advanced validation logic for user onboarding",
						commitMessage: `This commit introduces a comprehensive validation layer for the user onboarding flow.  
The primary goal is to ensure that all input data is strictly validated before being processed by downstream services.  
This improves system reliability and significantly reduces runtime exceptions in the registration pipeline.

The validation logic includes:

1. Email format checking using RFC 5322-compliant patterns.
2. Username length and character limitation enforcement.
3. Password strength enforcement, including:
   - Minimum length checks  
   - Mixed character type detection  
   - Optional entropy-based scoring
4. Optional phone number validation using region-specific rules.
`,
					},
				},

Review comment (refactor):
Is it really a good result?

```
* Refactor user service
...
- Supports templating
* (others...)
```

Not sure whether GitHub does so. Even if GitHub does so, I don't think GitHub was right or we need to blindly follow GitHub

Code after:
					{
						userName:      user2.Name,
						commitMessage: "commit msg 1",
					},
					{
						userName:      user2.Name,
						commitMessage: "commit msg 2",
					},
				},
				expectedMessage: `* commit msg 1

* commit msg 2

`,
			},
			{
				name: "Multiple-line messages",
				commitInfos: []*commitInfo{
					{
						userName: user2.Name,
						commitMessage: `commit msg 1

Commit description.`,
					},
					{
						userName: user2.Name,
						commitMessage: `commit msg 2

- Detail 1
- Detail 2`,
					},
				},
				expectedMessage: `* commit msg 1

Commit description.

* commit msg 2

- Detail 1
- Detail 2

`,
			},
			{
				name: "Too long message",
				commitInfos: []*commitInfo{
					{
						userName:      user2.Name,
						commitMessage: `loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong message`,
					},
				},
Repository: go-gitea/gitea
File: tests/integration/api_actions_permission_test.go (Go)

Code before:
		AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
	resp := MakeRequest(t, req, http.StatusOK)

	var u api.User
	DecodeJSON(t, resp, &u)
	assert.Equal(t, "gitea-actions", u.UserName)
}

func TestActionUserAccessPublicRepo(t *testing.T) {
	defer tests.PrepareTestEnv(t)()

	req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/raw/README.md").
		AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
	resp := MakeRequest(t, req, http.StatusOK)
	assert.Equal(t, "file", resp.Header().Get("x-gitea-object-type"))

	defer test.MockVariableValue(&setting.Service.RequireSignInViewStrict, true)()

	req = NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/raw/README.md").
		AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
	resp = MakeRequest(t, req, http.StatusOK)
	assert.Equal(t, "file", resp.Header().Get("x-gitea-object-type"))
}

func TestActionUserNoAccessOtherPrivateRepo(t *testing.T) {
	defer tests.PrepareTestEnv(t)()

	req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo2/raw/README.md").
		AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
	MakeRequest(t, req, http.StatusNotFound)
}


Review comment (suggestion):
The tests can be merged into one and use sub-tests, then only one `defer tests.PrepareTestEnv(t)()` is needed.

It will speed up the CI tests (in the past year, the testing time has increased about a few minute, eg: MSSQL: 12m -> 16m). So it's good to reduce the testing time if it is not difficult to do so.

Code after:

	var u api.User
	DecodeJSON(t, resp, &u)
	assert.Equal(t, "gitea-actions", u.UserName)
}

func testActionUserAccessPublicRepo(t *testing.T) {
	req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/raw/README.md").
		AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
	resp := MakeRequest(t, req, http.StatusOK)
	assert.Equal(t, "file", resp.Header().Get("x-gitea-object-type"))

	defer test.MockVariableValue(&setting.Service.RequireSignInViewStrict, true)()

	req = NewRequestf(t, "GET", "/api/v1/repos/user2/repo1/raw/README.md").
		AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
	resp = MakeRequest(t, req, http.StatusOK)
	assert.Equal(t, "file", resp.Header().Get("x-gitea-object-type"))
}

func testActionUserNoAccessOtherPrivateRepo(t *testing.T) {
	req := NewRequestf(t, "GET", "/api/v1/repos/user2/repo2/raw/README.md").
		AddTokenAuth("8061e833a55f6fc0157c98b883e91fcfeeb1a71a")
	MakeRequest(t, req, http.StatusNotFound)
}

func TestActionUserAccessPermission(t *testing.T) {
	defer tests.PrepareTestEnv(t)()

	t.Run("ActionUserSignIn", testActionUserSignIn)
	t.Run("ActionUserAccessPublicRepo", testActionUserAccessPublicRepo)
	t.Run("ActionUserNoAccessOtherPrivateRepo", testActionUserNoAccessOtherPrivateRepo)
}
Repository: go-gitea/gitea
File: services/mailer/sender/sender.go (Go)

Code before:

package sender

import (
	"errors"
	"io"
)

type Sender interface {
	Send(from string, to []string, msg io.WriterTo) error
}

var Send = send

func send(sender Sender, msg *Message) error {
	m := msg.ToMessage()
	froms := m.GetFrom()
	to, err := m.GetRecipients()
	if err != nil {
		return err
	}

	// TODO: implement sending from multiple addresses
	if len(froms) == 0 {
		return errors.New("no FROM specified")
	}
	return sender.Send(froms[0].Address, to, m)
}


Review comment (suggestion):
```suggestion
		// FIXME: no idea why sometimes the "froms" can be empty, need to figure out the root problem
		return errors.New("no FROM specified")
```

Code after:

package sender

import (
	"errors"
	"io"
)

type Sender interface {
	Send(from string, to []string, msg io.WriterTo) error
}

var Send = send

func send(sender Sender, msg *Message) error {
	m := msg.ToMessage()
	froms := m.GetFrom()
	to, err := m.GetRecipients()
	if err != nil {
		return err
	}

	// TODO: implement sending from multiple addresses
	if len(froms) == 0 {
		// FIXME: no idea why sometimes the "froms" can be empty, need to figure out the root problem
		return errors.New("no FROM specified")
	}
	return sender.Send(froms[0].Address, to, m)
}