hunk.code_window: 			Description          string
   			RootHTML             string
   			MainCSS              string
   			ManifestJSON         string
   			Subpath              string
   			ExpectedRootHTML     string
   			ExpectedMainCSS      string
   			ExpectedManifestJSON string
   		}{
   			{
  
hunk.labels: keep keep keep keep add keep keep keep keep keep
hunk.after_edit: 			ExpectedError        error
  
hunk.file_path: utils/subpath_test.go
hunk.type: add
file: package plugin_test
  
  import (
  	"strings"
  	"sync"
  
  	"github.com/pkg/errors"
  
  	"github.com/mattermost/mattermost-server/model"
  	"github.com/mattermost/mattermost-server/plugin"
  )
  
  // configuration represents the configuration for this plugin as exposed via the Mattermost
  // server configuration.
  type configuration struct {
  	TeamName    string
  	ChannelName string
  
  	// channelId is resolved when the public configuration fields above change
  	channelId string
  }
  
  type HelpPlugin struct {
  	plugin.MattermostPlugin
  
  	// configurationLock synchronizes access to the configuration.
  	configurationLock sync.RWMutex
  
  	// configuration is the active plugin configuration. Consult getConfiguration and
  	// setConfiguration for usage.
  	configuration *configuration
  }
  
  // getConfiguration retrieves the active configuration under lock, making it safe to use
  // concurrently. The active configuration may change underneath the client of this method, but
  // the struct returned by this API call is considered immutable.
  func (p *HelpPlugin) getConfiguration() *configuration {
  	p.configurationLock.RLock()
  	defer p.configurationLock.RUnlock()
  
  	if p.configuration == nil {
  		return &configuration{}
  	}
  
  	return p.configuration
  }
  
  // setConfiguration replaces the active configuration under lock.
  //
  // Do not call setConfiguration while holding the configurationLock, as sync.Mutex is not
  // reentrant.
  func (p *HelpPlugin) setConfiguration(configuration *configuration) {
  	// Replace the active configuration under lock.
  	p.configurationLock.Lock()
  	defer p.configurationLock.Unlock()
  	p.configuration = configuration
  }
  
  // OnConfigurationChange updates the active configuration for this plugin under lock.
  func (p *HelpPlugin) OnConfigurationChange() error {
  	var configuration = new(configuration)
  
  	// Load the public configuration fields from the Mattermost server configuration.
  	if err := p.API.LoadPluginConfiguration(configuration); err != nil {
  		return errors.Wrap(err, "failed to load plugin configuration")
  	}
  
  	team, err := p.API.GetTeamByName(configuration.TeamName)
  	if err != nil {
  		return errors.Wrapf(err, "failed to find team %s", configuration.TeamName)
  	}
  
  	channel, err := p.API.GetChannelByName(configuration.ChannelName, team.Id, false)
  	if err != nil {
  		return errors.Wrapf(err, "failed to find channel %s", configuration.ChannelName)
  	}
  
  	configuration.channelId = channel.Id
  
  	p.setConfiguration(configuration)
  
  	return nil
  }
  
  func (p *HelpPlugin) MessageHasBeenPosted(c *plugin.Context, post *model.Post) {
  	configuration := p.getConfiguration()
  
  	// Ignore posts not in the configured channel
  	if post.ChannelId != configuration.channelId {
  		return
  	}
  
  	// Ignore posts this plugin made.
  	if sentByPlugin, _ := post.Props["sent_by_plugin"].(bool); sentByPlugin {
  		return
  	}
  
  	// Ignore posts without a plea for help.
  	if !strings.Contains(post.Message, "help") {
  		return
  	}
  
  	p.API.SendEphemeralPost(post.UserId, &model.Post{
  		ChannelId: configuration.channelId,
  		Message:   "You asked for help? Checkout https://about.mattermost.com/help/",
  		Props: map[string]interface{}{
  			"sent_by_plugin": true,
  		},
  	})
  }
  
  func Example_helpPlugin() {
  	plugin.ClientMain(&HelpPlugin{})
  }
  